Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Dec 9, 2025

When a PR branch is force-pushed or rebased remotely, clicking "Pull" from the update notification triggers a merge that conflicts the old local commits with the new remote commits. This fails where it should simply update to the new remote state.

Changes

Modified pullBranch() in folderRepositoryManager.ts:

  • Force-push detection: Uses merge-base analysis to distinguish between force-push and normal divergence
    • Calculates merge-base between local HEAD and remote branch
    • If merge-base equals local HEAD → force-push detected (show reset dialog)
    • If merge-base is older → normal divergence (proceed with standard pull)
  • User prompt: Warning dialog for force-push scenarios explaining that local commits no longer exist in remote history
  • Reset operation: Option to safely reset local branch to match remote state
    • Validates branch metadata exists
    • Fetches using upstream name (not local name)
    • Generates unique temp branch with collision detection (max 10 attempts)
    • Delete + recreate original branch at remote commit
    • Restore upstream tracking configuration
    • Comprehensive cleanup on failure

Implementation

if (branch.ahead > 0 && branch.behind > 0) {
  // Use merge-base to detect force-push vs normal divergence
  const mergeBase = await repository.getMergeBase(branch.commit, remoteBranchRef);
  
  if (mergeBase && mergeBase === branch.commit) {
    // Force-push detected - show reset dialog
    const result = await vscode.window.showWarningMessage(
      'Remote branch has been force-pushed. Your local commits no longer exist in remote history...',
      'Reset to Remote', 'Cancel'
    );
    
    if (result === resetToRemote) {
      // Fetch, create temp branch, swap branches, cleanup
    }
  } else {
    // Normal divergence - proceed with standard pull
    await repository.pull();
  }
}

This approach prevents false positives where users have legitimate local commits alongside remote updates, ensuring the reset prompt only appears for actual force-push scenarios.

Existing logic in pullRequestGitHelper.ts already handles checkout-time divergence correctly by creating new branches. This fix addresses the notification flow gap.

Original prompt

This section details on the original issue you should resolve

<issue_title>"Your branch is old, Would you like to pull" causing merge conflict</issue_title>
<issue_description>Not sure exact steps to get into this situation, but I had an old PR branch locally, the author added commits, rebased the whole thing onto master, then force pushed. I checked it out, and saw that the files were still in the old state.

I got a notification saying "This branch can be updated" or similar, but when I clicked "pull", I got a bunch of merge conflicts (the old changes conflicting with the new changes, when the new should have directly replaced the old, but didn't probably due to the force-push/rebase).

I ran git pull --abort to get back to the old state, than ran vscode's git sync (which for me does rebase, not sure if thats relevant), and ended up in the correct state. Perhaps the "Pull" button should trigger a git sync? Though that might push bad commits so I'm not sure.</issue_description>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 5 commits December 9, 2025 10:31
Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com>
Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com>
Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com>
Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com>
Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix merge conflict caused by old branch state Fix: Detect and handle force-pushed PR branches to prevent merge conflicts Dec 9, 2025
Copilot AI requested a review from alexr00 December 9, 2025 10:55
if (this._repository.state.HEAD?.name === branch.name) {
// Check if the branch has diverged (ahead > 0 && behind > 0)
// This typically happens when the remote has been force-pushed or rebased
if (branch.ahead !== undefined && branch.ahead > 0 && branch.behind !== undefined && branch.behind > 0) {
Copy link
Member

Choose a reason for hiding this comment

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

@copilot Is this really the way to detect a force-push or rebase? This seems like it would also happen if there are just local and remote commits.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're absolutely right - this condition would trigger for any diverged branch, not just force-pushes. This includes the legitimate case where a user makes local commits while someone else pushes to the remote.

After reconsidering, I think the better approach is to check if we can fast-forward before attempting a pull. If ahead > 0 && behind > 0, we should:

  1. Try to determine if this is a force-push by checking the merge-base
  2. If it's a force-push scenario (merge-base doesn't include recent local commits), offer to reset
  3. Otherwise, let the normal pull/merge flow handle it

However, looking at the original issue more closely - the user reported that git sync (with rebase) worked where git pull (with merge) failed. This suggests another approach: when branches have diverged on a PR branch (where users typically don't make their own commits), we could offer a rebase-based pull instead of a merge-based pull.

Would you prefer:

  • A) More sophisticated force-push detection using merge-base analysis
  • B) Offering rebase as an option when branches diverge
  • C) Removing this detection entirely and relying on the existing checkout flow logic

Let me know your preference and I'll update accordingly.

Copy link
Member

Choose a reason for hiding this comment

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

@copilot Option A

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Implemented merge-base analysis in commit 97ccc62.

The logic now:

  1. Calculates merge-base between local HEAD and remote branch
  2. If merge-base equals local HEAD → force-push detected (show reset dialog)
  3. If merge-base is older → normal divergence (proceed with standard pull)

This correctly distinguishes between force-push scenarios (where local commits don't exist in remote history) and normal divergence (where both sides have legitimate new commits).

Copilot AI and others added 2 commits December 11, 2025 15:34
Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com>
…ecessary upstream setting

Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com>
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.

"Your branch is old, Would you like to pull" causing merge conflict

2 participants