-
Couldn't load subscription status.
- Fork 22
[FEAT][LSP] Worktrees #649
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 16 commits
c595a7a
b955ad8
4e3f9ec
50df6d7
e32e93f
ff78ce3
203002d
d041dfb
6a7da80
d8eb121
b6d6d40
6fb1544
48d6ccd
e33ff21
22b5065
9c41957
8b74abd
1500869
bdf1770
32099b4
6e619aa
7fccd60
8addeb3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,14 +9,16 @@ | |
| from functools import cache | ||
| from io import StringIO | ||
| from pathlib import Path | ||
| from typing import TYPE_CHECKING | ||
| from typing import TYPE_CHECKING, Optional | ||
|
|
||
| import git | ||
| from rich.prompt import Confirm | ||
| from unidiff import PatchSet | ||
|
|
||
| from codeflash.cli_cmds.console import logger | ||
| from codeflash.code_utils.compat import codeflash_cache_dir | ||
| from codeflash.code_utils.config_consts import N_CANDIDATES | ||
| from codeflash.lsp.helpers import is_LSP_enabled | ||
|
|
||
| if TYPE_CHECKING: | ||
| from git import Repo | ||
|
|
@@ -192,3 +194,84 @@ def get_last_commit_author_if_pr_exists(repo: Repo | None = None) -> str | None: | |
| return None | ||
| else: | ||
| return last_commit.author.name | ||
|
|
||
|
|
||
| worktree_dirs = codeflash_cache_dir / "worktrees" | ||
| patches_dir = codeflash_cache_dir / "patches" | ||
|
|
||
|
|
||
| def create_worktree_snapshot_commit(worktree_dir: Path, commit_message: str) -> None: | ||
| repository = git.Repo(worktree_dir, search_parent_directories=True) | ||
| repository.git.commit("-am", commit_message, "--no-verify") | ||
|
|
||
|
|
||
| def create_detached_worktree(module_root: Path) -> Optional[Path]: | ||
| if not check_running_in_git_repo(module_root): | ||
| logger.warning("Module is not in a git repository. Skipping worktree creation.") | ||
| return None | ||
| git_root = git_root_dir() | ||
| current_time_str = time.strftime("%Y%m%d-%H%M%S") | ||
| worktree_dir = worktree_dirs / f"{git_root.name}-{current_time_str}" | ||
|
|
||
| result = subprocess.run( | ||
| ["git", "worktree", "add", "-d", str(worktree_dir)], | ||
| cwd=git_root, | ||
| check=True, | ||
| stdout=subprocess.DEVNULL if is_LSP_enabled() else None, | ||
| stderr=subprocess.DEVNULL if is_LSP_enabled() else None, | ||
| ) | ||
| if result.returncode != 0: | ||
| logger.error(f"Failed to create worktree: {result.stderr}") | ||
| return None | ||
|
||
|
|
||
| # Get uncommitted diff from the original repo | ||
| repository = git.Repo(module_root, search_parent_directories=True) | ||
| repository.git.add("-N", ".") # add the index for untracked files to be included in the diff | ||
| uni_diff_text = repository.git.diff(None, "HEAD", ignore_blank_lines=True, ignore_space_at_eol=True) | ||
|
|
||
| if not uni_diff_text.strip(): | ||
| logger.info("No uncommitted changes to copy to worktree.") | ||
| return worktree_dir | ||
|
|
||
| # Write the diff to a temporary file | ||
| with tempfile.NamedTemporaryFile(mode="w+", suffix=".codeflash.patch", delete=False) as tmp_patch_file: | ||
| tmp_patch_file.write(uni_diff_text + "\n") # the new line here is a must otherwise the last hunk won't be valid | ||
| tmp_patch_file.flush() | ||
|
|
||
| patch_path = Path(tmp_patch_file.name).resolve() | ||
|
||
|
|
||
| # Apply the patch inside the worktree | ||
| try: | ||
| subprocess.run(["git", "apply", patch_path], cwd=worktree_dir, check=True) | ||
| create_worktree_snapshot_commit(worktree_dir, "Initial Snapshot") | ||
| except subprocess.CalledProcessError as e: | ||
| logger.error(f"Failed to apply patch to worktree: {e}") | ||
|
|
||
| return worktree_dir | ||
|
|
||
|
|
||
| def remove_worktree(worktree_dir: Path) -> None: | ||
| try: | ||
| repository = git.Repo(worktree_dir, search_parent_directories=True) | ||
| repository.git.worktree("remove", "--force", worktree_dir) | ||
| except Exception: | ||
| logger.exception(f"Failed to remove worktree: {worktree_dir}") | ||
|
|
||
|
|
||
| def create_diff_patch_from_worktree(worktree_dir: Path, files: list[str], fto_name: str) -> Path: | ||
| repository = git.Repo(worktree_dir, search_parent_directories=True) | ||
| uni_diff_text = repository.git.diff(None, "HEAD", *files, ignore_blank_lines=True, ignore_space_at_eol=True) | ||
|
|
||
| if not uni_diff_text: | ||
| logger.warning("No changes found in worktree.") | ||
| return None | ||
|
|
||
| if not uni_diff_text.endswith("\n"): | ||
| uni_diff_text += "\n" | ||
|
|
||
| # write to patches_dir | ||
| patches_dir.mkdir(parents=True, exist_ok=True) | ||
| patch_path = patches_dir / f"{worktree_dir.name}.{fto_name}.patch" | ||
| with patch_path.open("w", encoding="utf8") as f: | ||
| f.write(uni_diff_text) | ||
| return patch_path | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +0,0 @@ | ||
| # Silence the console module to prevent stdout pollution | ||
| from codeflash.cli_cmds.console import console | ||
|
|
||
| console.quiet = True | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
⚡️Codeflash found 8,048% (80.48x) speedup for
generate_candidatesincodeflash/code_utils/coverage_utils.py⏱️ Runtime :
162 milliseconds→1.99 milliseconds(best of365runs)📝 Explanation and details
The optimized code achieves a 81x speedup by eliminating expensive Path object operations in the main loop. The key optimization replaces the original approach that repeatedly calls
current_path.parentand constructsPath(current_path.name) / last_addedobjects with a more efficient strategy usingsource_code_path.parts.Key Changes:
.parentcalls, the code extracts all path components once usingsource_code_path.partsstr(Path(current_path.name) / last_added)operation (96% of original runtime) is replaced with simple string formattingf"{parts[i]}/{last_added}"while current_path != current_path.parentloop is replaced with aforloop that iterates through pre-computed partsWhy This Is Faster:
parts[i]) is faster than repeated method calls (.parent)Performance Characteristics:
The optimization shows excellent scaling - deeper directory structures see greater speedups (up to 104x faster for 1000-level nesting). All test cases benefit significantly, with the smallest improvement being 30% for simple cases and massive gains of 8000%+ for deeply nested paths.
✅ Correctness verification report:
⚙️ Existing Unit Tests and Runtime
test_code_utils.py::test_generate_candidates🌀 Generated Regression Tests and Runtime
🔎 Concolic Coverage Tests and Runtime
codeflash_concolic_9d591alw/tmpkrdmrlwv/test_concolic_coverage.py::test_generate_candidatesTo test or edit this optimization locally
git merge codeflash/optimize-pr649-2025-08-14T13.45.27