difit 2.0.11 → 2.1.1
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.
- package/README.ja.md +26 -9
- package/README.ko.md +20 -3
- package/README.md +20 -3
- package/README.zh.md +26 -9
- package/dist/cli/index.js +50 -0
- package/dist/cli/index.test.js +141 -12
- package/dist/cli/utils.js +2 -2
- package/dist/cli/utils.test.js +10 -1
- package/dist/client/assets/index-CueWm3qS.css +1 -0
- package/dist/client/assets/index-U2EYNr4O.js +215 -0
- package/dist/client/assets/{prism-csharp-Dc46Fjt0.js → prism-csharp-B2ldtMsY.js} +1 -1
- package/dist/client/assets/{prism-java-CqBdPW_L.js → prism-java-n1ck9mYw.js} +1 -1
- package/dist/client/assets/{prism-php-BLhwjsTl.js → prism-php-DiCmaEbX.js} +1 -1
- package/dist/client/assets/prism-protobuf-DiQ_z8B5.js +1 -0
- package/dist/client/assets/{prism-ruby-ExhPumJe.js → prism-ruby-qa8qRdnq.js} +1 -1
- package/dist/client/assets/{prism-solidity-BCgmGzF-.js → prism-solidity-DDmtWXtT.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/server/file-watcher.d.ts +23 -0
- package/dist/server/file-watcher.js +236 -0
- package/dist/server/file-watcher.test.d.ts +1 -0
- package/dist/server/file-watcher.test.js +225 -0
- package/dist/server/git-diff.d.ts +2 -0
- package/dist/server/git-diff.js +47 -4
- package/dist/server/git-diff.test.js +209 -0
- package/dist/server/server.d.ts +5 -2
- package/dist/server/server.js +66 -16
- package/dist/server/server.test.js +3 -3
- package/dist/types/watch.d.ts +30 -0
- package/dist/types/watch.js +8 -0
- package/package.json +3 -1
- package/dist/client/assets/index-B6vRltPu.css +0 -1
- package/dist/client/assets/index-CjocZrF8.js +0 -200
package/README.ja.md
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
<a href="./README.md">English</a> | 日本語 | <a href="./README.zh.md">简体中文</a> | <a href="./README.ko.md">한국어</a>
|
|
7
7
|
</p>
|
|
8
8
|
|
|
9
|
+

|
|
10
|
+
|
|
9
11
|
**difit**は、ローカルのgit上にある差分をGitHub風のビューアで閲覧・レビューできるCLIツールです。見やすい表示に加え、コメントはAIへのプロンプトとしてコピーできます。AI時代のローカルコードレビューツール!
|
|
10
12
|
|
|
11
13
|
## ✨ 機能
|
|
@@ -41,7 +43,7 @@ npx difit feature # featureブランチの最新コミット
|
|
|
41
43
|
### 2つのコミットを比較
|
|
42
44
|
|
|
43
45
|
```bash
|
|
44
|
-
npx difit
|
|
46
|
+
npx difit @ main # mainブランチと比較(@はHEADのエイリアス)
|
|
45
47
|
npx difit feature main # ブランチ間を比較
|
|
46
48
|
npx difit . origin/main # 作業ディレクトリとリモートmainを比較
|
|
47
49
|
```
|
|
@@ -76,6 +78,21 @@ Enterprise ServerのPRを表示する場合、あなたのEnterprise Serverイ
|
|
|
76
78
|
2. 適切なスコープでパーソナルアクセストークンを生成
|
|
77
79
|
3. `GITHUB_TOKEN`環境変数として設定
|
|
78
80
|
|
|
81
|
+
### 標準入力
|
|
82
|
+
|
|
83
|
+
パイプを使用して標準入力経由で統一diff形式を渡すことで、任意のツールからのdiffをdifitで表示できます。
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
# 他のツールからのdiffを表示
|
|
87
|
+
diff -u file1.txt file2.txt | npx difit
|
|
88
|
+
|
|
89
|
+
# 保存されたパッチをレビュー
|
|
90
|
+
cat changes.patch | npx difit
|
|
91
|
+
|
|
92
|
+
# マージベースとの比較
|
|
93
|
+
git diff --merge-base main feature | npx difit
|
|
94
|
+
```
|
|
95
|
+
|
|
79
96
|
## ⚙️ CLIオプション
|
|
80
97
|
|
|
81
98
|
| フラグ | デフォルト | 説明 |
|
|
@@ -83,7 +100,7 @@ Enterprise ServerのPRを表示する場合、あなたのEnterprise Serverイ
|
|
|
83
100
|
| `<target>` | HEAD | コミットハッシュ、タグ、HEAD~n、ブランチ、または特別な引数 |
|
|
84
101
|
| `[compare-with]` | - | 比較対象の2番目のコミット(2つの間のdiffを表示) |
|
|
85
102
|
| `--pr <url>` | - | レビューするGitHub PRのURL(例:https://github.com/owner/repo/pull/123) |
|
|
86
|
-
| `--port` |
|
|
103
|
+
| `--port` | 4966 | 優先ポート。使用中の場合は+1にフォールバック |
|
|
87
104
|
| `--host` | 127.0.0.1 | サーバーをバインドするホストアドレス(外部からアクセスしたい場合は0.0.0.0を指定) |
|
|
88
105
|
| `--no-open` | false | ブラウザを自動的に開かない |
|
|
89
106
|
| `--mode` | side-by-side | 表示モード。inline`または`side-by-side` |
|
|
@@ -116,13 +133,13 @@ src/components/Button.tsx:L42-L48 # この行が自動的に追加されます
|
|
|
116
133
|
|
|
117
134
|
## 🎨 シンタックスハイライト対応言語
|
|
118
135
|
|
|
119
|
-
- **JavaScript/TypeScript**:`.js
|
|
120
|
-
- **Web技術**:HTML
|
|
121
|
-
- **シェルスクリプト**:`.sh
|
|
122
|
-
- **バックエンド言語**:PHP
|
|
123
|
-
- **システム言語**:C
|
|
124
|
-
- **モバイル言語**:Swift
|
|
125
|
-
- **その他**:Python
|
|
136
|
+
- **JavaScript/TypeScript**:`.js`, `.jsx`, `.ts`, `.tsx`
|
|
137
|
+
- **Web技術**:HTML, CSS, JSON, XML, Markdown
|
|
138
|
+
- **シェルスクリプト**:`.sh`, `.bash`, `.zsh`, `.fish`
|
|
139
|
+
- **バックエンド言語**:PHP, SQL, Ruby, Java, Scala
|
|
140
|
+
- **システム言語**:C, C++, C#, Rust, Go
|
|
141
|
+
- **モバイル言語**:Swift, Kotlin, Dart
|
|
142
|
+
- **その他**:Python, Protobuf, YAML, Solidity, Vim Script
|
|
126
143
|
|
|
127
144
|
## 🛠️ 開発
|
|
128
145
|
|
package/README.ko.md
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
<a href="./README.md">English</a> | <a href="./README.ja.md">日本語</a> | <a href="./README.zh.md">简体中文</a> | 한국어
|
|
7
7
|
</p>
|
|
8
8
|
|
|
9
|
+

|
|
10
|
+
|
|
9
11
|
**difit**은 GitHub 스타일 뷰어로 로컬 git diff를 보고 검토할 수 있는 CLI 도구입니다. 깔끔한 시각적 효과와 함께 코멘트를 AI용 프롬프트로 복사할 수 있습니다. AI 시대의 로컬 코드 리뷰 도구!
|
|
10
12
|
|
|
11
13
|
## ✨ 기능
|
|
@@ -41,7 +43,7 @@ npx difit feature # feature 브랜치의 최신 커밋
|
|
|
41
43
|
### 두 커밋 비교
|
|
42
44
|
|
|
43
45
|
```bash
|
|
44
|
-
npx difit
|
|
46
|
+
npx difit @ main # main 브랜치와 비교 (@는 HEAD의 별칭)
|
|
45
47
|
npx difit feature main # 브랜치 간 비교
|
|
46
48
|
npx difit . origin/main # 작업 디렉토리와 원격 main 비교
|
|
47
49
|
```
|
|
@@ -76,6 +78,21 @@ Enterprise Server PR의 경우 귀하의 Enterprise Server 인스턴스에서
|
|
|
76
78
|
2. 적절한 범위로 개인 액세스 토큰 생성
|
|
77
79
|
3. `GITHUB_TOKEN` 환경 변수로 설정
|
|
78
80
|
|
|
81
|
+
### 표준 입력
|
|
82
|
+
|
|
83
|
+
파이프를 사용하여 표준 입력을 통해 통합 diff를 전달하면 모든 도구의 diff를 difit으로 볼 수 있습니다.
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
# 다른 도구의 diff 보기
|
|
87
|
+
diff -u file1.txt file2.txt | npx difit
|
|
88
|
+
|
|
89
|
+
# 저장된 패치 검토
|
|
90
|
+
cat changes.patch | npx difit
|
|
91
|
+
|
|
92
|
+
# 머지 베이스와 비교
|
|
93
|
+
git diff --merge-base main feature | npx difit
|
|
94
|
+
```
|
|
95
|
+
|
|
79
96
|
## ⚙️ CLI 옵션
|
|
80
97
|
|
|
81
98
|
| 플래그 | 기본값 | 설명 |
|
|
@@ -83,7 +100,7 @@ Enterprise Server PR의 경우 귀하의 Enterprise Server 인스턴스에서
|
|
|
83
100
|
| `<target>` | HEAD | 커밋 해시, 태그, HEAD~n, 브랜치 또는 특수 인수 |
|
|
84
101
|
| `[compare-with]` | - | 비교할 선택적 두 번째 커밋 (둘 사이의 diff 표시) |
|
|
85
102
|
| `--pr <url>` | - | 검토할 GitHub PR URL (예: https://github.com/owner/repo/pull/123) |
|
|
86
|
-
| `--port` |
|
|
103
|
+
| `--port` | 4966 | 선호 포트; 사용 중인 경우 +1로 대체 |
|
|
87
104
|
| `--host` | 127.0.0.1 | 서버를 바인딩할 호스트 주소 (외부 액세스는 0.0.0.0 사용) |
|
|
88
105
|
| `--no-open` | false | 브라우저를 자동으로 열지 않음 |
|
|
89
106
|
| `--mode` | side-by-side | 표시 모드: `inline` 또는 `side-by-side` |
|
|
@@ -122,7 +139,7 @@ src/components/Button.tsx:L42-L48 # 이 줄은 자동으로 추가됩니다
|
|
|
122
139
|
- **백엔드 언어**: PHP, SQL, Ruby, Java, Scala
|
|
123
140
|
- **시스템 언어**: C, C++, C#, Rust, Go
|
|
124
141
|
- **모바일 언어**: Swift, Kotlin, Dart
|
|
125
|
-
- **기타**: Python, YAML, Solidity, Vim 스크립트
|
|
142
|
+
- **기타**: Python, Protobuf, YAML, Solidity, Vim 스크립트
|
|
126
143
|
|
|
127
144
|
## 🛠️ 개발
|
|
128
145
|
|
package/README.md
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
English | <a href="./README.ja.md">日本語</a> | <a href="./README.zh.md">简体中文</a> | <a href="./README.ko.md">한국어</a>
|
|
7
7
|
</p>
|
|
8
8
|
|
|
9
|
+

|
|
10
|
+
|
|
9
11
|
**difit** is a CLI tool that lets you view and review local git diffs with a GitHub-style viewer. In addition to clean visuals, comments can be copied as prompts for AI. The local code review tool for the AI era!
|
|
10
12
|
|
|
11
13
|
## ✨ Features
|
|
@@ -41,7 +43,7 @@ npx difit feature # Latest commit on feature branch
|
|
|
41
43
|
### Compare two commits
|
|
42
44
|
|
|
43
45
|
```bash
|
|
44
|
-
npx difit
|
|
46
|
+
npx difit @ main # Compare with main branch (@ is alias for HEAD)
|
|
45
47
|
npx difit feature main # Compare branches
|
|
46
48
|
npx difit . origin/main # Compare working directory with remote main
|
|
47
49
|
```
|
|
@@ -76,6 +78,21 @@ For Enterprise Server PRs, you must set a token generated on YOUR Enterprise Ser
|
|
|
76
78
|
2. Generate a personal access token with appropriate scopes
|
|
77
79
|
3. Set it as `GITHUB_TOKEN` environment variable
|
|
78
80
|
|
|
81
|
+
### Stdin
|
|
82
|
+
|
|
83
|
+
By using a pipe to pass unified diffs via stdin, you can view diffs from any tool with difit.
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
# View diffs from other tools
|
|
87
|
+
diff -u file1.txt file2.txt | npx difit
|
|
88
|
+
|
|
89
|
+
# Review saved patches
|
|
90
|
+
cat changes.patch | npx difit
|
|
91
|
+
|
|
92
|
+
# Compare against merge base
|
|
93
|
+
git diff --merge-base main feature | npx difit
|
|
94
|
+
```
|
|
95
|
+
|
|
79
96
|
## ⚙️ CLI Options
|
|
80
97
|
|
|
81
98
|
| Flag | Default | Description |
|
|
@@ -83,7 +100,7 @@ For Enterprise Server PRs, you must set a token generated on YOUR Enterprise Ser
|
|
|
83
100
|
| `<target>` | HEAD | Commit hash, tag, HEAD~n, branch, or special arguments |
|
|
84
101
|
| `[compare-with]` | - | Optional second commit to compare with (shows diff between the two) |
|
|
85
102
|
| `--pr <url>` | - | GitHub PR URL to review (e.g., https://github.com/owner/repo/pull/123) |
|
|
86
|
-
| `--port` |
|
|
103
|
+
| `--port` | 4966 | Preferred port; falls back to +1 if occupied |
|
|
87
104
|
| `--host` | 127.0.0.1 | Host address to bind server to (use 0.0.0.0 for external access) |
|
|
88
105
|
| `--no-open` | false | Don't automatically open browser |
|
|
89
106
|
| `--mode` | side-by-side | Display mode: `inline` or `side-by-side` |
|
|
@@ -122,7 +139,7 @@ This section is unnecessary
|
|
|
122
139
|
- **Backend Languages**: PHP, SQL, Ruby, Java, Scala
|
|
123
140
|
- **Systems Languages**: C, C++, C#, Rust, Go
|
|
124
141
|
- **Mobile Languages**: Swift, Kotlin, Dart
|
|
125
|
-
- **Others**: Python, YAML, Solidity, Vim script
|
|
142
|
+
- **Others**: Python, Protobuf, YAML, Solidity, Vim script
|
|
126
143
|
|
|
127
144
|
## 🛠️ Development
|
|
128
145
|
|
package/README.zh.md
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
<a href="./README.md">English</a> | <a href="./README.ja.md">日本語</a> | 简体中文 | <a href="./README.ko.md">한국어</a>
|
|
7
7
|
</p>
|
|
8
8
|
|
|
9
|
+

|
|
10
|
+
|
|
9
11
|
**difit** 是一个让你使用 GitHub 风格查看器查看和审查本地 git 差异的 CLI 工具。除了清晰的视觉效果外,评论还可以作为 AI 提示进行复制。AI 时代的本地代码审查工具!
|
|
10
12
|
|
|
11
13
|
## ✨ 功能
|
|
@@ -41,7 +43,7 @@ npx difit feature # feature 分支上的最新提交
|
|
|
41
43
|
### 比较两个提交
|
|
42
44
|
|
|
43
45
|
```bash
|
|
44
|
-
npx difit
|
|
46
|
+
npx difit @ main # 与 main 分支比较(@ 是 HEAD 的别名)
|
|
45
47
|
npx difit feature main # 比较分支
|
|
46
48
|
npx difit . origin/main # 比较工作目录与远程 main
|
|
47
49
|
```
|
|
@@ -76,6 +78,21 @@ difit 使用以下方式自动处理 GitHub 认证:
|
|
|
76
78
|
2. 生成具有适当范围的个人访问令牌
|
|
77
79
|
3. 将其设置为 `GITHUB_TOKEN` 环境变量
|
|
78
80
|
|
|
81
|
+
### 标准输入
|
|
82
|
+
|
|
83
|
+
通过使用管道通过标准输入传递统一差异,您可以使用 difit 查看来自任何工具的差异。
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
# 查看来自其他工具的差异
|
|
87
|
+
diff -u file1.txt file2.txt | npx difit
|
|
88
|
+
|
|
89
|
+
# 审查保存的补丁
|
|
90
|
+
cat changes.patch | npx difit
|
|
91
|
+
|
|
92
|
+
# 与合并基础比较
|
|
93
|
+
git diff --merge-base main feature | npx difit
|
|
94
|
+
```
|
|
95
|
+
|
|
79
96
|
## ⚙️ CLI 选项
|
|
80
97
|
|
|
81
98
|
| 标志 | 默认值 | 描述 |
|
|
@@ -83,7 +100,7 @@ difit 使用以下方式自动处理 GitHub 认证:
|
|
|
83
100
|
| `<target>` | HEAD | 提交哈希、标签、HEAD~n、分支或特殊参数 |
|
|
84
101
|
| `[compare-with]` | - | 要比较的可选第二个提交(显示两者之间的差异) |
|
|
85
102
|
| `--pr <url>` | - | 要审查的 GitHub PR URL(例如:https://github.com/owner/repo/pull/123) |
|
|
86
|
-
| `--port` |
|
|
103
|
+
| `--port` | 4966 | 首选端口;如果被占用则回退到 +1 |
|
|
87
104
|
| `--host` | 127.0.0.1 | 绑定服务器的主机地址(使用 0.0.0.0 进行外部访问) |
|
|
88
105
|
| `--no-open` | false | 不自动打开浏览器 |
|
|
89
106
|
| `--mode` | side-by-side | 显示模式:`inline` 或 `side-by-side` |
|
|
@@ -116,13 +133,13 @@ src/components/Button.tsx:L42-L48 # 此行自动添加
|
|
|
116
133
|
|
|
117
134
|
## 🎨 语法高亮语言
|
|
118
135
|
|
|
119
|
-
- **JavaScript/TypeScript**:`.js
|
|
120
|
-
- **Web 技术**:HTML
|
|
121
|
-
- **Shell 脚本**:`.sh
|
|
122
|
-
- **后端语言**:PHP
|
|
123
|
-
- **系统语言**:C
|
|
124
|
-
- **移动语言**:Swift
|
|
125
|
-
- **其他**:Python
|
|
136
|
+
- **JavaScript/TypeScript**:`.js`, `.jsx`, `.ts`, `.tsx`
|
|
137
|
+
- **Web 技术**:HTML, CSS, JSON, XML, Markdown
|
|
138
|
+
- **Shell 脚本**:`.sh`, `.bash`, `.zsh`, `.fish`
|
|
139
|
+
- **后端语言**:PHP, SQL, Ruby, Java, Scala
|
|
140
|
+
- **系统语言**:C, C++, C#, Rust, Go
|
|
141
|
+
- **移动语言**:Swift, Kotlin, Dart
|
|
142
|
+
- **其他**:Python, Protobuf, YAML, Solidity, Vim Script
|
|
126
143
|
|
|
127
144
|
## 🛠️ 开发
|
|
128
145
|
|
package/dist/cli/index.js
CHANGED
|
@@ -4,10 +4,28 @@ import React from 'react';
|
|
|
4
4
|
import { simpleGit } from 'simple-git';
|
|
5
5
|
import pkg from '../../package.json' with { type: 'json' };
|
|
6
6
|
import { startServer } from '../server/server.js';
|
|
7
|
+
import { DiffMode } from '../types/watch.js';
|
|
7
8
|
import { findUntrackedFiles, markFilesIntentToAdd, promptUser, validateDiffArguments, resolvePrCommits, } from './utils.js';
|
|
8
9
|
function isSpecialArg(arg) {
|
|
9
10
|
return arg === 'working' || arg === 'staged' || arg === '.';
|
|
10
11
|
}
|
|
12
|
+
function determineDiffMode(targetCommitish, compareWith) {
|
|
13
|
+
// If comparing specific commits/branches (not involving HEAD), no watching needed
|
|
14
|
+
if (compareWith && targetCommitish !== 'HEAD') {
|
|
15
|
+
return DiffMode.SPECIFIC;
|
|
16
|
+
}
|
|
17
|
+
if (targetCommitish === 'working') {
|
|
18
|
+
return DiffMode.WORKING;
|
|
19
|
+
}
|
|
20
|
+
if (targetCommitish === 'staged') {
|
|
21
|
+
return DiffMode.STAGED;
|
|
22
|
+
}
|
|
23
|
+
if (targetCommitish === '.') {
|
|
24
|
+
return DiffMode.DOT;
|
|
25
|
+
}
|
|
26
|
+
// Default mode: HEAD^ vs HEAD or HEAD vs other commits (watch for HEAD changes)
|
|
27
|
+
return DiffMode.DEFAULT;
|
|
28
|
+
}
|
|
11
29
|
const program = new Command();
|
|
12
30
|
program
|
|
13
31
|
.name('difit')
|
|
@@ -24,6 +42,29 @@ program
|
|
|
24
42
|
.option('--clean', 'start with a clean slate by clearing all existing comments')
|
|
25
43
|
.action(async (commitish, compareWith, options) => {
|
|
26
44
|
try {
|
|
45
|
+
// Check if we should read from stdin
|
|
46
|
+
const shouldReadStdin = !process.stdin.isTTY || commitish === '-';
|
|
47
|
+
if (shouldReadStdin) {
|
|
48
|
+
// Read unified diff from stdin
|
|
49
|
+
const diffContent = await readStdin();
|
|
50
|
+
if (!diffContent.trim()) {
|
|
51
|
+
console.error('Error: No diff content received from stdin');
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
// Start server with stdin diff
|
|
55
|
+
const { url } = await startServer({
|
|
56
|
+
stdinDiff: diffContent,
|
|
57
|
+
preferredPort: options.port,
|
|
58
|
+
host: options.host,
|
|
59
|
+
openBrowser: options.open,
|
|
60
|
+
mode: options.mode,
|
|
61
|
+
clearComments: options.clean,
|
|
62
|
+
});
|
|
63
|
+
console.log(`\n🚀 difit server started on ${url}`);
|
|
64
|
+
console.log(`📋 Reviewing: diff from stdin`);
|
|
65
|
+
console.log('\nPress Ctrl+C to stop the server');
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
27
68
|
// Determine target and base commitish
|
|
28
69
|
let targetCommitish = commitish;
|
|
29
70
|
let baseCommitish;
|
|
@@ -88,6 +129,7 @@ program
|
|
|
88
129
|
process.exit(1);
|
|
89
130
|
}
|
|
90
131
|
}
|
|
132
|
+
const diffMode = determineDiffMode(targetCommitish, compareWith);
|
|
91
133
|
const { url, port, isEmpty } = await startServer({
|
|
92
134
|
targetCommitish,
|
|
93
135
|
baseCommitish,
|
|
@@ -96,6 +138,7 @@ program
|
|
|
96
138
|
openBrowser: options.open,
|
|
97
139
|
mode: options.mode,
|
|
98
140
|
clearComments: options.clean,
|
|
141
|
+
diffMode,
|
|
99
142
|
});
|
|
100
143
|
console.log(`\n🚀 difit server started on ${url}`);
|
|
101
144
|
console.log(`📋 Reviewing: ${targetCommitish}`);
|
|
@@ -137,6 +180,13 @@ program
|
|
|
137
180
|
});
|
|
138
181
|
program.parse();
|
|
139
182
|
// Check for untracked files and prompt user to add them for diff visibility
|
|
183
|
+
async function readStdin() {
|
|
184
|
+
const chunks = [];
|
|
185
|
+
for await (const chunk of process.stdin) {
|
|
186
|
+
chunks.push(chunk);
|
|
187
|
+
}
|
|
188
|
+
return Buffer.concat(chunks).toString('utf8');
|
|
189
|
+
}
|
|
140
190
|
async function handleUntrackedFiles(git) {
|
|
141
191
|
const files = await findUntrackedFiles(git);
|
|
142
192
|
if (files.length === 0) {
|
package/dist/cli/index.test.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
4
|
+
import { DiffMode } from '../types/watch.js';
|
|
4
5
|
// Mock all external dependencies
|
|
5
6
|
vi.mock('simple-git');
|
|
6
7
|
vi.mock('../server/server.js');
|
|
@@ -37,8 +38,8 @@ describe('CLI index.ts', () => {
|
|
|
37
38
|
vi.mocked(simpleGit).mockReturnValue(mockGit);
|
|
38
39
|
mockStartServer = vi.mocked(startServer);
|
|
39
40
|
mockStartServer.mockResolvedValue({
|
|
40
|
-
port:
|
|
41
|
-
url: 'http://localhost:
|
|
41
|
+
port: 4966,
|
|
42
|
+
url: 'http://localhost:4966',
|
|
42
43
|
isEmpty: false,
|
|
43
44
|
});
|
|
44
45
|
mockPromptUser = vi.mocked(promptUser);
|
|
@@ -421,8 +422,8 @@ describe('CLI index.ts', () => {
|
|
|
421
422
|
it('displays clean message when flag is used', async () => {
|
|
422
423
|
mockFindUntrackedFiles.mockResolvedValue([]);
|
|
423
424
|
mockStartServer.mockResolvedValue({
|
|
424
|
-
port:
|
|
425
|
-
url: 'http://localhost:
|
|
425
|
+
port: 4966,
|
|
426
|
+
url: 'http://localhost:4966',
|
|
426
427
|
isEmpty: false,
|
|
427
428
|
});
|
|
428
429
|
const program = new Command();
|
|
@@ -461,8 +462,8 @@ describe('CLI index.ts', () => {
|
|
|
461
462
|
it('does not display clean message when flag is not used', async () => {
|
|
462
463
|
mockFindUntrackedFiles.mockResolvedValue([]);
|
|
463
464
|
mockStartServer.mockResolvedValue({
|
|
464
|
-
port:
|
|
465
|
-
url: 'http://localhost:
|
|
465
|
+
port: 4966,
|
|
466
|
+
url: 'http://localhost:4966',
|
|
466
467
|
isEmpty: false,
|
|
467
468
|
});
|
|
468
469
|
const program = new Command();
|
|
@@ -503,8 +504,8 @@ describe('CLI index.ts', () => {
|
|
|
503
504
|
it('displays server startup message with correct URL', async () => {
|
|
504
505
|
mockFindUntrackedFiles.mockResolvedValue([]);
|
|
505
506
|
mockStartServer.mockResolvedValue({
|
|
506
|
-
port:
|
|
507
|
-
url: 'http://localhost:
|
|
507
|
+
port: 4966,
|
|
508
|
+
url: 'http://localhost:4966',
|
|
508
509
|
isEmpty: false,
|
|
509
510
|
});
|
|
510
511
|
const program = new Command();
|
|
@@ -540,14 +541,14 @@ describe('CLI index.ts', () => {
|
|
|
540
541
|
}
|
|
541
542
|
});
|
|
542
543
|
await program.parseAsync([], { from: 'user' });
|
|
543
|
-
expect(console.log).toHaveBeenCalledWith('\n🚀 difit server started on http://localhost:
|
|
544
|
+
expect(console.log).toHaveBeenCalledWith('\n🚀 difit server started on http://localhost:4966');
|
|
544
545
|
expect(console.log).toHaveBeenCalledWith('📋 Reviewing: HEAD');
|
|
545
546
|
});
|
|
546
547
|
it('displays correct message when no differences found', async () => {
|
|
547
548
|
mockFindUntrackedFiles.mockResolvedValue([]);
|
|
548
549
|
mockStartServer.mockResolvedValue({
|
|
549
|
-
port:
|
|
550
|
-
url: 'http://localhost:
|
|
550
|
+
port: 4966,
|
|
551
|
+
url: 'http://localhost:4966',
|
|
551
552
|
isEmpty: true,
|
|
552
553
|
});
|
|
553
554
|
const program = new Command();
|
|
@@ -578,7 +579,7 @@ describe('CLI index.ts', () => {
|
|
|
578
579
|
});
|
|
579
580
|
await program.parseAsync([], { from: 'user' });
|
|
580
581
|
expect(console.log).toHaveBeenCalledWith('\n! No differences found. Browser will not open automatically.');
|
|
581
|
-
expect(console.log).toHaveBeenCalledWith(' Server is running at http://localhost:
|
|
582
|
+
expect(console.log).toHaveBeenCalledWith(' Server is running at http://localhost:4966 if you want to check manually.\n');
|
|
582
583
|
});
|
|
583
584
|
});
|
|
584
585
|
describe('Server mode option handling', () => {
|
|
@@ -812,4 +813,132 @@ describe('CLI index.ts', () => {
|
|
|
812
813
|
expect(process.exit).toHaveBeenCalledWith(1);
|
|
813
814
|
});
|
|
814
815
|
});
|
|
816
|
+
describe('Diff mode determination', () => {
|
|
817
|
+
const testCases = [
|
|
818
|
+
{
|
|
819
|
+
name: 'determines DEFAULT mode for HEAD',
|
|
820
|
+
args: ['HEAD'],
|
|
821
|
+
expectedMode: 'default',
|
|
822
|
+
},
|
|
823
|
+
{
|
|
824
|
+
name: 'determines WORKING mode for working',
|
|
825
|
+
args: ['working'],
|
|
826
|
+
expectedMode: 'working',
|
|
827
|
+
},
|
|
828
|
+
{
|
|
829
|
+
name: 'determines STAGED mode for staged',
|
|
830
|
+
args: ['staged'],
|
|
831
|
+
expectedMode: 'staged',
|
|
832
|
+
},
|
|
833
|
+
{
|
|
834
|
+
name: 'determines DOT mode for dot argument',
|
|
835
|
+
args: ['.'],
|
|
836
|
+
expectedMode: 'dot',
|
|
837
|
+
},
|
|
838
|
+
{
|
|
839
|
+
name: 'determines SPECIFIC mode for commit comparison',
|
|
840
|
+
args: ['abc123', 'def456'],
|
|
841
|
+
expectedMode: 'specific',
|
|
842
|
+
},
|
|
843
|
+
{
|
|
844
|
+
name: 'determines DEFAULT mode for custom commit',
|
|
845
|
+
args: ['main'],
|
|
846
|
+
expectedMode: 'default',
|
|
847
|
+
},
|
|
848
|
+
];
|
|
849
|
+
testCases.forEach(({ name, args, expectedMode }) => {
|
|
850
|
+
it(name, async () => {
|
|
851
|
+
mockFindUntrackedFiles.mockResolvedValue([]);
|
|
852
|
+
const program = new Command();
|
|
853
|
+
program
|
|
854
|
+
.argument('[commit-ish]', 'commit-ish', 'HEAD')
|
|
855
|
+
.argument('[compare-with]', 'compare-with')
|
|
856
|
+
.option('--port <port>', 'port', parseInt)
|
|
857
|
+
.option('--host <host>', 'host', '')
|
|
858
|
+
.option('--no-open', 'no-open')
|
|
859
|
+
.option('--mode <mode>', 'mode', 'side-by-side')
|
|
860
|
+
.option('--tui', 'tui')
|
|
861
|
+
.option('--pr <url>', 'pr')
|
|
862
|
+
.action(async (commitish, compareWith, options) => {
|
|
863
|
+
// Simulate determineDiffMode function behavior
|
|
864
|
+
let diffMode;
|
|
865
|
+
if (compareWith && commitish !== 'HEAD') {
|
|
866
|
+
diffMode = DiffMode.SPECIFIC;
|
|
867
|
+
}
|
|
868
|
+
else if (commitish === 'working') {
|
|
869
|
+
diffMode = DiffMode.WORKING;
|
|
870
|
+
}
|
|
871
|
+
else if (commitish === 'staged') {
|
|
872
|
+
diffMode = DiffMode.STAGED;
|
|
873
|
+
}
|
|
874
|
+
else if (commitish === '.') {
|
|
875
|
+
diffMode = DiffMode.DOT;
|
|
876
|
+
}
|
|
877
|
+
else {
|
|
878
|
+
diffMode = DiffMode.DEFAULT;
|
|
879
|
+
}
|
|
880
|
+
await startServer({
|
|
881
|
+
targetCommitish: commitish,
|
|
882
|
+
baseCommitish: compareWith || commitish + '^',
|
|
883
|
+
preferredPort: options.port,
|
|
884
|
+
host: options.host,
|
|
885
|
+
openBrowser: options.open,
|
|
886
|
+
mode: options.mode,
|
|
887
|
+
diffMode,
|
|
888
|
+
});
|
|
889
|
+
});
|
|
890
|
+
await program.parseAsync(args, { from: 'user' });
|
|
891
|
+
expect(mockStartServer).toHaveBeenCalledWith(expect.objectContaining({
|
|
892
|
+
diffMode: expectedMode,
|
|
893
|
+
}));
|
|
894
|
+
});
|
|
895
|
+
});
|
|
896
|
+
it('handles HEAD comparison with different commit', async () => {
|
|
897
|
+
mockFindUntrackedFiles.mockResolvedValue([]);
|
|
898
|
+
const program = new Command();
|
|
899
|
+
program
|
|
900
|
+
.argument('[commit-ish]', 'commit-ish', 'HEAD')
|
|
901
|
+
.argument('[compare-with]', 'compare-with')
|
|
902
|
+
.option('--port <port>', 'port', parseInt)
|
|
903
|
+
.option('--host <host>', 'host', '')
|
|
904
|
+
.option('--no-open', 'no-open')
|
|
905
|
+
.option('--mode <mode>', 'mode', 'side-by-side')
|
|
906
|
+
.option('--tui', 'tui')
|
|
907
|
+
.option('--pr <url>', 'pr')
|
|
908
|
+
.action(async (commitish, compareWith, options) => {
|
|
909
|
+
// Simulate determineDiffMode function behavior
|
|
910
|
+
let diffMode;
|
|
911
|
+
if (compareWith && commitish !== 'HEAD') {
|
|
912
|
+
diffMode = DiffMode.SPECIFIC;
|
|
913
|
+
}
|
|
914
|
+
else if (commitish === 'working') {
|
|
915
|
+
diffMode = DiffMode.WORKING;
|
|
916
|
+
}
|
|
917
|
+
else if (commitish === 'staged') {
|
|
918
|
+
diffMode = DiffMode.STAGED;
|
|
919
|
+
}
|
|
920
|
+
else if (commitish === '.') {
|
|
921
|
+
diffMode = DiffMode.DOT;
|
|
922
|
+
}
|
|
923
|
+
else {
|
|
924
|
+
diffMode = DiffMode.DEFAULT;
|
|
925
|
+
}
|
|
926
|
+
await startServer({
|
|
927
|
+
targetCommitish: commitish,
|
|
928
|
+
baseCommitish: compareWith || commitish + '^',
|
|
929
|
+
preferredPort: options.port,
|
|
930
|
+
host: options.host,
|
|
931
|
+
openBrowser: options.open,
|
|
932
|
+
mode: options.mode,
|
|
933
|
+
diffMode,
|
|
934
|
+
});
|
|
935
|
+
});
|
|
936
|
+
await program.parseAsync(['HEAD', 'main'], { from: 'user' });
|
|
937
|
+
expect(mockStartServer).toHaveBeenCalledWith(expect.objectContaining({
|
|
938
|
+
diffMode: 'default', // HEAD with comparison is still DEFAULT mode
|
|
939
|
+
targetCommitish: 'HEAD',
|
|
940
|
+
baseCommitish: 'main',
|
|
941
|
+
}));
|
|
942
|
+
});
|
|
943
|
+
});
|
|
815
944
|
});
|
package/dist/cli/utils.js
CHANGED
|
@@ -21,6 +21,7 @@ export function validateCommitish(commitish) {
|
|
|
21
21
|
/^[a-f0-9]{4,40}\^+$/i, // SHA hashes with ^ suffix (parent references)
|
|
22
22
|
/^[a-f0-9]{4,40}~\d+$/i, // SHA hashes with ~N suffix (ancestor references)
|
|
23
23
|
/^HEAD(~\d+|\^\d*)*$/, // HEAD, HEAD~1, HEAD^, HEAD^2, etc.
|
|
24
|
+
/^@(~\d+|\^\d*)*$/, // @, @~1, @^, @^2, etc. (@ is Git alias for HEAD)
|
|
24
25
|
];
|
|
25
26
|
// Check if it matches any specific patterns first
|
|
26
27
|
if (validPatterns.some((pattern) => pattern.test(trimmed))) {
|
|
@@ -35,8 +36,7 @@ function isValidBranchName(name) {
|
|
|
35
36
|
return false; // Cannot start with dash
|
|
36
37
|
if (name.endsWith('.'))
|
|
37
38
|
return false; // Cannot end with dot
|
|
38
|
-
|
|
39
|
-
return false; // Cannot be just @
|
|
39
|
+
// @ is a valid Git alias for HEAD, so we should allow it
|
|
40
40
|
if (name.includes('..'))
|
|
41
41
|
return false; // No consecutive dots
|
|
42
42
|
if (name.includes('@{'))
|
package/dist/cli/utils.test.js
CHANGED
|
@@ -27,6 +27,15 @@ describe('CLI Utils', () => {
|
|
|
27
27
|
expect(validateCommitish('HEAD^2')).toBe(true);
|
|
28
28
|
expect(validateCommitish('HEAD~2^1')).toBe(true);
|
|
29
29
|
});
|
|
30
|
+
it('should validate @ references (Git alias for HEAD)', () => {
|
|
31
|
+
expect(validateCommitish('@')).toBe(true);
|
|
32
|
+
expect(validateCommitish('@~1')).toBe(true);
|
|
33
|
+
expect(validateCommitish('@~10')).toBe(true);
|
|
34
|
+
expect(validateCommitish('@^')).toBe(true);
|
|
35
|
+
expect(validateCommitish('@^1')).toBe(true);
|
|
36
|
+
expect(validateCommitish('@^2')).toBe(true);
|
|
37
|
+
expect(validateCommitish('@~2^1')).toBe(true);
|
|
38
|
+
});
|
|
30
39
|
it('should validate branch names', () => {
|
|
31
40
|
// Valid branch names according to git rules
|
|
32
41
|
expect(validateCommitish('main')).toBe(true);
|
|
@@ -56,7 +65,7 @@ describe('CLI Utils', () => {
|
|
|
56
65
|
// Invalid branch names according to git rules
|
|
57
66
|
expect(validateCommitish('-feature')).toBe(false); // cannot start with dash
|
|
58
67
|
expect(validateCommitish('feature.')).toBe(false); // cannot end with dot
|
|
59
|
-
expect(validateCommitish('@')).toBe(
|
|
68
|
+
expect(validateCommitish('@')).toBe(true); // @ is a valid Git alias for HEAD
|
|
60
69
|
expect(validateCommitish('feature..test')).toBe(false); // no consecutive dots
|
|
61
70
|
expect(validateCommitish('feature@{upstream}')).toBe(false); // no @{ sequence
|
|
62
71
|
expect(validateCommitish('feature//test')).toBe(false); // no consecutive slashes
|