Skip to content

Commit 7a7bfc6

Browse files
committed
feat: implement commit history search
1 parent 0985e16 commit 7a7bfc6

3 files changed

Lines changed: 61 additions & 6 deletions

File tree

wimygit-tauri/src-tauri/src/git/parsers/history.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ pub async fn get_history(
117117
skip: u32,
118118
count: u32,
119119
all: bool,
120+
grep: Option<String>,
120121
) -> Result<Vec<CommitInfo>, String> {
121122
let format_str = format!("{}%H||%h||%an||%at||%s||%D||%P", COMMIT_MARKER);
122123
let mut args = vec![
@@ -130,6 +131,13 @@ pub async fn get_history(
130131
args.insert(1, "--all".to_string());
131132
}
132133

134+
if let Some(ref pattern) = grep {
135+
if !pattern.is_empty() {
136+
args.push(format!("--grep={}", pattern));
137+
args.push("--regexp-ignore-case".to_string());
138+
}
139+
}
140+
133141
if !path.is_empty() {
134142
args.push("--".to_string());
135143
args.push(path);

wimygit-tauri/src/components/tabs/HistoryTab.tsx

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,10 @@ export function HistoryTab({ repoPath, filePath, refreshKey, onRefresh, onFileSe
247247
const [hasMore, setHasMore] = useState(true);
248248
const [error, setError] = useState<string | null>(null);
249249

250+
const [searchInput, setSearchInput] = useState("");
251+
const [searchQuery, setSearchQuery] = useState("");
252+
const searchRef = useRef<HTMLInputElement>(null);
253+
250254
const [selectedCommit, setSelectedCommit] = useState<CommitInfo | null>(null);
251255
const [commitFiles, setCommitFiles] = useState<CommitFile[]>([]);
252256
const [parents, setParents] = useState<string[]>([]);
@@ -265,7 +269,7 @@ export function HistoryTab({ repoPath, filePath, refreshKey, onRefresh, onFileSe
265269
if (!repoPath) return;
266270
skip === 0 ? setLoading(true) : setLoadingMore(true);
267271
try {
268-
const result = await getHistory(repoPath, filePath ?? "", skip, PAGE_SIZE, allBranches);
272+
const result = await getHistory(repoPath, filePath ?? "", skip, PAGE_SIZE, allBranches, searchQuery || undefined);
269273
setCommits((prev) => skip === 0 ? result : [...prev, ...result]);
270274
setHasMore(result.length === PAGE_SIZE);
271275
setError(null);
@@ -275,7 +279,7 @@ export function HistoryTab({ repoPath, filePath, refreshKey, onRefresh, onFileSe
275279
setLoading(false);
276280
setLoadingMore(false);
277281
}
278-
}, [repoPath, filePath, allBranches]);
282+
}, [repoPath, filePath, allBranches, searchQuery]);
279283

280284
useEffect(() => {
281285
setSelectedCommit(null);
@@ -334,8 +338,8 @@ export function HistoryTab({ repoPath, filePath, refreshKey, onRefresh, onFileSe
334338
) !== "/";
335339

336340
const graphRows = useMemo(
337-
() => isPathFiltered ? computeLinearLayout(commits.length) : computeGraphLayout(commits),
338-
[commits, isPathFiltered]
341+
() => (isPathFiltered || !!searchQuery) ? computeLinearLayout(commits.length) : computeGraphLayout(commits),
342+
[commits, isPathFiltered, searchQuery]
339343
);
340344

341345
// ── render ────────────────────────────────────────────────────────────────
@@ -382,6 +386,48 @@ export function HistoryTab({ repoPath, filePath, refreshKey, onRefresh, onFileSe
382386
All Branches
383387
</label>
384388
</div>
389+
{/* ── Search bar ── */}
390+
<div className="flex items-center gap-2 px-3 py-1 border-b border-gray-200 dark:border-gray-700 shrink-0">
391+
<button
392+
onClick={() => setSearchQuery(searchInput)}
393+
className="text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 shrink-0"
394+
title="Search"
395+
>
396+
<svg width="12" height="12" viewBox="0 0 16 16" fill="currentColor">
397+
<path d="M10.68 11.74a6 6 0 0 1-7.922-8.982 6 6 0 0 1 8.982 7.922l3.04 3.04a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215ZM11.5 7a4.499 4.499 0 1 0-8.997 0A4.499 4.499 0 0 0 11.5 7Z" />
398+
</svg>
399+
</button>
400+
<input
401+
ref={searchRef}
402+
type="text"
403+
value={searchInput}
404+
onChange={(e) => setSearchInput(e.target.value)}
405+
onKeyDown={(e) => {
406+
if (e.key === "Enter") setSearchQuery(searchInput);
407+
if (e.key === "Escape") { setSearchInput(""); setSearchQuery(""); }
408+
}}
409+
placeholder="Search commits..."
410+
className="flex-1 min-w-0 bg-transparent text-xs text-gray-700 dark:text-gray-300 placeholder-gray-400 outline-none"
411+
/>
412+
{searchQuery && (
413+
loading ? (
414+
<span className="text-xs text-gray-400 shrink-0">...</span>
415+
) : (
416+
<span className="text-xs text-gray-400 shrink-0">{commits.length}{hasMore ? "+" : ""} results</span>
417+
)
418+
)}
419+
{searchInput && (
420+
<button
421+
onClick={() => { setSearchInput(""); setSearchQuery(""); }}
422+
className="shrink-0 text-gray-400 hover:text-gray-600 dark:hover:text-gray-200"
423+
title="Clear search"
424+
>
425+
<svg width="12" height="12" viewBox="0 0 16 16" fill="currentColor">
426+
<path d="M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734L9.06 8l3.22 3.22a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L8 9.06l-3.22 3.22a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06Z" />
427+
</svg>
428+
</button>
429+
)}
430+
</div>
385431
{/* ── Top: commit list ── */}
386432
<div className="flex-[3] overflow-y-auto border-b border-gray-200 dark:border-gray-700">
387433
{commits.map((commit, idx) => {

wimygit-tauri/src/lib/git-api.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -292,9 +292,10 @@ export async function getHistory(
292292
path: string = "",
293293
skip: number = 0,
294294
count: number = 100,
295-
all: boolean = true
295+
all: boolean = true,
296+
grep?: string,
296297
): Promise<CommitInfo[]> {
297-
return invoke<CommitInfo[]>("get_history", { cwd, path, skip, count, all });
298+
return invoke<CommitInfo[]>("get_history", { cwd, path, skip, count, all, grep: grep ?? null });
298299
}
299300

300301
export async function getCommitFiles(

0 commit comments

Comments
 (0)