- Notifications
You must be signed in to change notification settings - Fork14
Fix branch error handling#468
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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
base:master
Are you sure you want to change the base?
Uh oh!
There was an error while loading.Please reload this page.
Conversation
…ser guidancewhy: When vcspull encounters branch-related errors (like local branches with noremote tracking), it fails with cryptic git error messages that don't help usersunderstand how to fix their configuration.what:- Add BRANCH_ERROR_MSG constant with clear, actionable error template- Add handle_branch_error() function to detect available remote branches- Enhance update_repo() to catch CommandError exceptions for branch issues- Show helpful instructions for adding 'rev' field to configuration- Display available remote branches when git is accessible- Gracefully handle cases where git commands failrefs: Addresses issue where 'vcspull sync zod' failed with "invalid upstream" errors
why: Need to ensure branch error detection and user guidance works correctlyacross different scenarios and edge cases.what:- Add test_handle_branch_error() to verify error message content and branch detection- Add test_handle_branch_error_no_git() to test graceful handling when git fails- Add test_update_repo_branch_error() for integration testing of error flow- Mock subprocess.run and GitSync.update_repo for controlled testing- Test error message formatting and available branch display- Verify CommandError exceptions are properly caught and re-raisedrefs: Tests for branch error handling feature implementation
…t all upstream issueswhy: The initial implementation only flagged branches that didn't exist on remote,but missed the more common case where branches exist remotely but lack upstreamtracking, which still causes libvcs to construct invalid remote references.what:- Update check_branch_state() to flag ANY branch without upstream tracking- This catches both cases: branches that don't exist remotely AND branches that exist remotely but have no upstream configured- Add comprehensive tests for both problematic and good branch states- Fix linting issues and improve code formatting- Test validates the fix works with real repository (zod) showing proper error detection and user guidancerefs: Fixes the root cause where vcspull sync would fail silently on brancheswith upstream tracking issues
sourcery-aibot commentedSep 4, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Reviewer's GuideAdds proactive branch error detection and user-friendly guidance for branch-related failures in vcspull sync operations, with pre-sync validation, fallback handling, and comprehensive tests. Class diagram for new branch error handling functionsclassDiagram class SyncHandler { +check_branch_state(repo_dict: dict) bool +handle_branch_error(repo_dict: dict) void +update_repo(repo_dict: dict, ...) Repo } SyncHandler <|.. check_branch_state SyncHandler <|.. handle_branch_error SyncHandler <|.. update_repoFile-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess yourdashboard to:
Getting Help
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@## master #468 +/- ##==========================================- Coverage 78.93% 78.58% -0.35%========================================== Files 8 8 Lines 413 467 +54 Branches 85 95 +10 ==========================================+ Hits 326 367 +41- Misses 52 59 +7- Partials 35 41 +6 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
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.
Hey there - I've reviewed your changes and they look great!
Blocking issues:
- Detected subprocess function 'run' without a static string. If this data can be controlled by a malicious actor, it may be an instance of command injection. Audit the use of this call to ensure it is not controllable by an external resource. You may consider using 'shlex.escape()'. (link)
Prompt for AI Agents
Please address the comments from this code review:##Individual Comments###Comment 1<location>`src/vcspull/cli/sync.py:225` </location><code_context>+ # Even if the remote branch exists, the lack of upstream tracking+ # can cause libvcs to construct invalid remote references+ # Check if there's a remote branch with the same name+ remote_ref = f"origin/{branch_name}"+ remote_branch = subprocess.run(+ ["git", "-C", str(repo_path), "rev-parse", "--verify", remote_ref],+ capture_output=True,</code_context><issue_to_address>Assumes 'origin' as remote; may not be correct for all repos.This approach may fail in repositories where the remote is not named 'origin'. Allow configuring the remote name or detect it automatically.Suggested implementation:```python# Even if the remote branch exists, the lack of upstream tracking# can cause libvcs to construct invalid remote references# Check if there's a remote branch with the same name# Detect remote name automatically remote_name_proc= subprocess.run( ["git","-C",str(repo_path),"remote"],capture_output=True,text=True,timeout=5,check=False, ) remote_name="origin"if remote_name_proc.returncode==0: remotes= remote_name_proc.stdout.strip().splitlines()if remotes: remote_name= remotes[0]# Use the first remote found remote_ref=f"{remote_name}/{branch_name}" remote_branch= subprocess.run( ["git","-C",str(repo_path),"rev-parse","--verify", remote_ref],capture_output=True,```If you want to allow the remote name to be configured (e.g., via a function argumentor environment variable), you should:1. Add a parameter to the relevant function to accept`remote_name`, defaulting toNone.2. If`remote_name`is provided, use it; otherwise, detectas above.3. Optionally, checkfor an environment variable (e.g.,`VCS_REMOTE_NAME`) before falling back to detection.You may need to propagate this parameter through function callsor configuration.</issue_to_address>### Comment 2<location>`src/vcspull/cli/sync.py:246`</location><code_context>+returnFalse# Git command failed or not available+++def handle_branch_error(repo_dict: dict[str, t.Any])->None:+"""Handle branch-related errors by showing helpful instructions.+</code_context><issue_to_address>Consider logging errors instead of only printing to stderr.Using the logger will improve error traceability and make integration with monitoring tools easier.Suggested implementation:```pythonimport loggingimport subprocessimport sysimport typing as tfrom copy import deepcopylogger = logging.getLogger(__name__)``````pythondef handle_branch_error(repo_dict: dict[str, t.Any]) -> None:"""Handle branch-related errors by showing helpful instructions. logger.error("Branch-related error detected for repository:%s\n""Please check your branch configuration and ensure upstream tracking is set correctly.", repo_dict.get("name","<unknown>") )```</issue_to_address>### Comment 3<location>`tests/test_sync.py:322`</location><code_context>+def test_check_branch_state(tmp_path: pathlib.Path)->None:</code_context><issue_to_address>Consider adding testsfor detachedHEADand missing repo path edge cases.Tests currently cover typical branch states butnot detachedHEADor missing'path'in repo_dict. Including these cases will improve coverageand error handling.</issue_to_address>### Comment 4<location>`tests/test_sync.py:411`</location><code_context>+def test_handle_branch_error(</code_context><issue_to_address>Test only checks first5 branches; consider parametrizingfor different branch counts.Parametrize the test to cover scenarioswith fewer, exactly,and more than5 branches to improve coverage.Suggested implementation:```pythonimport pytest@pytest.mark.parametrize("branches", [ ["main","dev"],# fewer than 5 ["main","dev","feature1","feature2","feature3"],# exactly 5 ["main","dev","feature1","feature2","feature3","hotfix"],# more than 5 ],)def test_handle_branch_error( branches, tmp_path: pathlib.Path, capsys: pytest.CaptureFixture[str])->None:"""Test that handle_branch_error shows helpful error message for different branch counts."""repo_dict= {"name":"test-repo","url":"git@github.com:user/repo.git","path": tmp_path/"test-repo", }# Create a mock git directory for testing# Simulate branches in the repo# You may need to adjust the following lines to fit your repo setup logicrepo_path= repo_dict["path"] repo_path.mkdir(parents=True,exist_ok=True) (repo_path/".git").mkdir()# Simulate branch list# If your code uses subprocess to get branches, you may need to patch subprocess.run# For now, we just show how to use the branches variable in the test# Example: pass branches to the function or mock the function that lists branches# ... rest of your test logic here, using the branches variable ...```You will need to adjust the test logic to use the`branches` variable, depending on how your code fetches branch names (e.g., patching subprocessor passing directly). If you use subprocess to get branches, consider patching it toreturn the`branches`listfor each test case.</issue_to_address>## Security Issues### Issue 1<location>`src/vcspull/cli/sync.py:262`</location><issue_to_address>**security (python.lang.security.audit.dangerous-subprocess-use-audit):** Detected subprocess function'run' without a static string. If this data can be controlled by a malicious actor, it may be an instance of command injection. Audit the use of this call to ensure itisnot controllable by an external resource. You may consider using'shlex.escape()'.*Source: opengrep*</issue_to_address>
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| remote_ref=f"origin/{branch_name}" | ||
| remote_branch=subprocess.run( |
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.
suggestion: Assumes 'origin' as remote; may not be correct for all repos.
This approach may fail in repositories where the remote is not named 'origin'. Allow configuring the remote name or detect it automatically.
Suggested implementation:
# Even if the remote branch exists, the lack of upstream tracking# can cause libvcs to construct invalid remote references# Check if there's a remote branch with the same name# Detect remote name automaticallyremote_name_proc=subprocess.run( ["git","-C",str(repo_path),"remote"],capture_output=True,text=True,timeout=5,check=False, )remote_name="origin"ifremote_name_proc.returncode==0:remotes=remote_name_proc.stdout.strip().splitlines()ifremotes:remote_name=remotes[0]# Use the first remote foundremote_ref=f"{remote_name}/{branch_name}"remote_branch=subprocess.run( ["git","-C",str(repo_path),"rev-parse","--verify",remote_ref],capture_output=True,
If you want to allow the remote name to be configured (e.g., via a function argument or environment variable), you should:
- Add a parameter to the relevant function to accept
remote_name, defaulting to None. - If
remote_nameis provided, use it; otherwise, detect as above. - Optionally, check for an environment variable (e.g.,
VCS_REMOTE_NAME) before falling back to detection.
You may need to propagate this parameter through function calls or configuration.
| returnFalse# Git command failed or not available | ||
| defhandle_branch_error(repo_dict:dict[str,t.Any])->None: |
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.
suggestion: Consider logging errors instead of only printing to stderr.
Using the logger will improve error traceability and make integration with monitoring tools easier.
Suggested implementation:
importloggingimportsubprocessimportsysimporttypingastfromcopyimportdeepcopylogger=logging.getLogger(__name__)
defhandle_branch_error(repo_dict:dict[str,t.Any])->None: """Handle branch-related errors by showing helpful instructions. logger.error( "Branch-related error detected for repository: %s\n" "Please check your branch configuration and ensure upstream tracking is set correctly.", repo_dict.get("name", "<unknown>") )
| deftest_check_branch_state(tmp_path:pathlib.Path)->None: | ||
| """Test that check_branch_state detects problematic branches.""" | ||
| repo_dict= { | ||
| "name":"test-repo", | ||
| "url":"git@github.com:user/repo.git", | ||
| "path":tmp_path/"test-repo", | ||
| } | ||
| # Create a mock git directory for testing | ||
| (tmp_path/"test-repo"/".git").mkdir(parents=True,exist_ok=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.
suggestion (testing): Consider adding tests for detached HEAD and missing repo path edge cases.
Tests currently cover typical branch states but not detached HEAD or missing 'path' in repo_dict. Including these cases will improve coverage and error handling.
| deftest_handle_branch_error( | ||
| tmp_path:pathlib.Path,capsys:pytest.CaptureFixture[str] | ||
| )->None: | ||
| """Test that handle_branch_error shows helpful error message.""" | ||
| repo_dict= { | ||
| "name":"test-repo", | ||
| "url":"git@github.com:user/repo.git", | ||
| "path":tmp_path/"test-repo", | ||
| } | ||
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.
suggestion (testing): Test only checks first 5 branches; consider parametrizing for different branch counts.
Parametrize the test to cover scenarios with fewer, exactly, and more than 5 branches to improve coverage.
Suggested implementation:
importpytest@pytest.mark.parametrize("branches", [ ["main","dev"],# fewer than 5 ["main","dev","feature1","feature2","feature3"],# exactly 5 ["main","dev","feature1","feature2","feature3","hotfix"],# more than 5 ],)deftest_handle_branch_error(branches,tmp_path:pathlib.Path,capsys:pytest.CaptureFixture[str])->None:"""Test that handle_branch_error shows helpful error message for different branch counts."""repo_dict= {"name":"test-repo","url":"git@github.com:user/repo.git","path":tmp_path/"test-repo", }# Create a mock git directory for testing# Simulate branches in the repo# You may need to adjust the following lines to fit your repo setup logicrepo_path=repo_dict["path"]repo_path.mkdir(parents=True,exist_ok=True) (repo_path/".git").mkdir()# Simulate branch list# If your code uses subprocess to get branches, you may need to patch subprocess.run# For now, we just show how to use the branches variable in the test# Example: pass branches to the function or mock the function that lists branches# ... rest of your test logic here, using the branches variable ...
You will need to adjust the test logic to use thebranches variable, depending on how your code fetches branch names (e.g., patching subprocess or passing directly). If you use subprocess to get branches, consider patching it to return thebranches list for each test case.
| result=subprocess.run( | ||
| cmd,capture_output=True,text=True,timeout=5,check=False | ||
| ) |
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.
security (python.lang.security.audit.dangerous-subprocess-use-audit): Detected subprocess function 'run' without a static string. If this data can be controlled by a malicious actor, it may be an instance of command injection. Audit the use of this call to ensure it is not controllable by an external resource. You may consider using 'shlex.escape()'.
Source: opengrep
| returnFalse | ||
| try: | ||
| # Check if we're in a git repository |
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.
issue (code-quality): We've found these issues:
- Swap if/else branches (
swap-if-else-branches) - Remove unnecessary else after guard condition (
remove-unnecessary-else) - Extract code out into function (
extract-method) - Lift code into else after jump in control flow (
reintroduce-else) - Hoist repeated code outside conditional statement (
hoist-statement-from-if)
| branches= [ | ||
| line.strip().replace("origin/","") | ||
| forlineinresult.stdout.splitlines() | ||
| if"origin/"inlineand"->"notinline | ||
| ][:5]# Show first 5 branches |
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.
issue (code-quality): Use named expression to simplify assignment and conditional (use-named-expression)
Uh oh!
There was an error while loading.Please reload this page.
Implements proactive branch error detection and helpful user guidance for vcspull sync operations. When users encounter branch-related sync issues, vcspull now provides clear, actionable instructions instead of cryptic git error messages.
Problem
Users experienced confusing failures when running
vcspull syncon repositories with branch issues:The root cause was that libvcs handles certain git errors internally without re-raising them, so vcspull never had a chance to provide helpful guidance.
Solution
Pre-Validation Approach
Added proactive branch state checking that detects problematic conditions before attempting sync operations:
check_branch_state(): Validates git repository branch configurationhandle_branch_error(): Shows clear, actionable error messagesUser Experience Improvement
Before:
After:
Technical Implementation
Core Components
Branch State Detection (
check_branch_state)Error Message Handler (
handle_branch_error)Integration (
update_repo)Error Detection Logic
The function detects problematic branch states by:
Any branch without proper upstream tracking is flagged as potentially problematic, as it can cause libvcs to construct invalid remote references.
Testing
Comprehensive Test Coverage
test_check_branch_state(): Tests detection of problematic branchestest_check_branch_state_good_branch(): Verifies good branches aren't flaggedtest_handle_branch_error(): Tests error message formatting and branch listingtest_handle_branch_error_no_git(): Tests graceful handling when git unavailabletest_update_repo_branch_error_precheck(): Tests pre-validation integrationtest_update_repo_branch_error_fallback(): Tests CommandError fallback handlingReal-World Validation
Tested with actual repository experiencing the issue:
❯ uv run vcspull sync zod⚠️ Error syncing'zod': Branch issue detected[...helpful guidance shown...]Quality Assurance
Configuration Example
Users can now easily fix branch issues by adding a
revfield:Backwards Compatibility
Files Changed
src/vcspull/cli/sync.py: Added branch detection and error handlingtests/test_sync.py: Added comprehensive test coverageBenefits
Summary by Sourcery
Improve vcspull sync error handling by proactively detecting branch configuration issues and providing actionable user guidance, integrated into the sync workflow with thorough test coverage.
New Features:
Enhancements:
Tests: