Skip to content

perf: speed up modified file lookup in large repositories - #544

Merged
cjappl merged 1 commit into
mainfrom
capple/faster_lookup
Jul 13, 2026
Merged

perf: speed up modified file lookup in large repositories#544
cjappl merged 1 commit into
mainfrom
capple/faster_lookup

Conversation

@cjappl

@cjappl cjappl commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

Check list

  • I have performed a self-review of my code
  • I have commented my code in hard-to-understand areas
  • I have added unit tests for my code

Description

Use a modified-file-only path for git-forgit restore (grs) and git-forgit checkout_file (gcf) so these interactive selectors do not scan large sets of untracked files. This keeps both commands responsive in repositories with many untracked files.

On a repo I use for work, comparing the raw git diff with the raw git ls-files for listing modified files is ~8x faster, the difference between ~1.5s and ~10s. The quality of life improvement is huge.

To get this change working when in sibling directories, I pulled out an existing perl script used in one of the other functions.

Type of change

  • Performance improvement
  • Refactor

Test environment

  • Shell
    • bash
    • zsh
    • fish
  • OS
    • Linux
    • Mac OS X
    • Windows
    • Others:

Summary by CodeRabbit

  • Bug Fixes

    • Improved modified-file listings when running from subdirectories, with correct sibling-directory relative display paths.
    • Updated restore and file checkout to use the new modified-file selection logic for more accurate results.
    • Ensured untracked files are excluded from “modified-only” outputs.
    • Standardized status row formatting and path display via shared cwd-relative normalization.
  • Tests

    • Added coverage for modified-file parity, cwd-relative path rewriting, status-entry formatting, and untracked-file filtering behavior.

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 00d5dbbb-91b0-4e27-8ac5-4b3594f958c7

📥 Commits

Reviewing files that changed from the base of the PR and between bc01d24 and 2688968.

📒 Files selected for processing (2)
  • bin/git-forgit
  • tests/working-tree-changes.test.sh
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/working-tree-changes.test.sh
  • bin/git-forgit

📝 Walkthrough

Walkthrough

Changes

Working-tree path handling

Layer / File(s) Summary
Path rewriting and modified-file listing
bin/git-forgit, tests/working-tree-changes.test.sh
Adds shared cwd-relative path rewriting and _forgit_list_modified_files, with coverage for modified, sibling-directory, and untracked-file cases.
Status-entry normalization
bin/git-forgit, tests/working-tree-changes.test.sh
Routes formatted status entries through the shared rewrite helper while preserving status metadata and repository-relative paths.
Restore and checkout integration
bin/git-forgit
Uses _forgit_list_modified_files for restore fallback candidates and checkout-file checks and selection.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant GitDiff
  participant ModifiedFiles
  participant PathRewriter
  participant RestoreCheckout
  GitDiff->>ModifiedFiles: Return null-delimited modified paths
  ModifiedFiles->>PathRewriter: Rewrite repository paths for cwd
  PathRewriter-->>ModifiedFiles: Return cwd-relative display paths
  ModifiedFiles->>RestoreCheckout: Provide selectable modified files
Loading

Possibly related PRs

  • wfxr/forgit#537: Both changes update how modified files are collected for the restore workflow.

Suggested reviewers: wfxr, carlfriedrich

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: faster modified-file lookup for large repositories.
Description check ✅ Passed The description follows the template and includes a summary, change type, checklist items, and test environment details.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch capple/faster_lookup

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread bin/git-forgit
}
}
' "$rootdir" "$cwd" "$mode" "$separator"
}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change was largely just pulled from _forgit_build_status_entries below, with a few ergonomic tweaks to make it work for both situations. The new tests should hopefully make sure this is stable

@cjappl

cjappl commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator Author

Also, hi! Long time no see. I see from my inbox you all have been super hard at work in this repo.

Please let me know if I've run afoul of any new norms or if I've missed anything.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
bin/git-forgit (1)

1131-1142: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Optional: capture the modified-file list once instead of invoking it twice.

_forgit_list_modified_files runs a full git diff + Perl pipeline at Line 1131 (count) and again at Line 1141 (fzf input). Since this PR targets responsiveness, capturing the result once avoids the redundant subprocess and closes the small window where the two calls could disagree.

♻️ Proposed single-invocation refactor
 _forgit_checkout_file() {
     _forgit_inside_work_tree || return 1
-    local files opts
+    local files opts modified_files
     _forgit_contains_non_flags "$@" && {
         _forgit_git_checkout_file "$@"
         return $?
     }
-    [[ $(_forgit_list_modified_files | wc -l) -eq 0 ]] && echo 'Nothing to checkout.' && return 1
+    modified_files=$(_forgit_list_modified_files)
+    [[ -z $modified_files ]] && echo 'Nothing to checkout.' && return 1
     opts="
         $FORGIT_FZF_DEFAULT_OPTS
         -m -0
         --preview=\"$FORGIT preview checkout_file_preview {}\"
         $FORGIT_CHECKOUT_FILE_FZF_OPTS
     "
     files=()
     while IFS='' read -r file; do
         files+=("$file")
-    done < <(_forgit_list_modified_files |
-        FZF_DEFAULT_OPTS="$opts" fzf)
+    done < <(printf '%s\n' "$modified_files" |
+        FZF_DEFAULT_OPTS="$opts" fzf)
     [[ ${`#files`[@]} -gt 0 ]] && _forgit_git_checkout_file "$@" "${files[@]}"
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@bin/git-forgit` around lines 1131 - 1142, Capture the output of
_forgit_list_modified_files once at the start of the checkout flow, use that
captured value for the empty-list check, and feed the same value into the fzf
input while preserving filename handling and array population. Remove the second
invocation so the modified-file list cannot change between checks.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@bin/git-forgit`:
- Around line 1131-1142: Capture the output of _forgit_list_modified_files once
at the start of the checkout flow, use that captured value for the empty-list
check, and feed the same value into the fzf input while preserving filename
handling and array population. Remove the second invocation so the modified-file
list cannot change between checks.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3b2ba7f4-5e0d-40af-bca2-fcadba08ddc4

📥 Commits

Reviewing files that changed from the base of the PR and between e0c7b84 and 2c97ddd.

📒 Files selected for processing (2)
  • bin/git-forgit
  • tests/working-tree-changes.test.sh

@cjappl

cjappl commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator Author

I like the coderabbit nitpick review, I think that's also a good improvement. I can either apply it here or do it in a follow up commit. Let me know what you think (human reviewers)

@sandr01d sandr01d left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good to see you here again!
I like this very much. I've run into git ls-files being slow on large repositories in the past too and this should give us a nice boost. Approved with two minor nitpicks.

Comment thread bin/git-forgit Outdated
@sandr01d

Copy link
Copy Markdown
Collaborator

I like the coderabbit nitpick review, I think that's also a good improvement. I can either apply it here or do it in a follow up commit. Let me know what you think (human reviewers)

I agree. As to whether it should go in a follow up commit or not, I'm fine with both. Maybe leaning a bit more towards having a separate commit because it is a different kind of optimization, but fine either way.

Comment thread bin/git-forgit Outdated
@cjappl

cjappl commented Jul 12, 2026

Copy link
Copy Markdown
Collaborator Author

I like the coderabbit nitpick review, I think that's also a good improvement. I can either apply it here or do it in a follow up commit. Let me know what you think (human reviewers)

I agree. As to whether it should go in a follow up commit or not, I'm fine with both. Maybe leaning a bit more towards having a separate commit because it is a different kind of optimization, but fine either way.

Cool, I lean to doing a follow on, so I will do that!

Thanks for the review, applied the two comment suggestions.

Use a modified-file-only path for `git-forgit restore` (`grs`) and
`git-forgit checkout_file` (`gcf`) so these interactive selectors do not scan
large sets of untracked files. This keeps both commands responsive in
repositories with many untracked files.
@cjappl
cjappl force-pushed the capple/faster_lookup branch from bc01d24 to 2688968 Compare July 12, 2026 19:23
@cjappl
cjappl merged commit a8e9d8b into main Jul 13, 2026
14 checks passed
@cjappl
cjappl deleted the capple/faster_lookup branch July 13, 2026 17:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants