Skip to content

[bug/5] Resolved push hanging when push fails#6

Merged
justinkumpe merged 1 commit intomainfrom
bug/5
Feb 13, 2026
Merged

[bug/5] Resolved push hanging when push fails#6
justinkumpe merged 1 commit intomainfrom
bug/5

Conversation

@justinkumpe
Copy link
Member

@justinkumpe justinkumpe commented Feb 13, 2026

resolves #5

Summary by Sourcery

Improve webview initialization and DOM-based rendering while making git push behavior more robust and interactive.

New Features:

  • Add webview readiness handshake so the extension only refreshes content after the webview is ready.
  • Support dynamic selection and configuration of git remotes, including adding or updating remotes via prompts when pushing or running remote-add.

Bug Fixes:

  • Prevent push commands from hanging by detecting missing upstreams, prompting for a remote, and handling push failures with user feedback.
  • Avoid errors when rendering the webview by guarding against missing data and DOM elements and centralizing error display.

Enhancements:

  • Refactor webview rendering from string-based innerHTML updates to DOM API usage with helper utilities for safer, more maintainable UI updates.
  • Unify push logic for add-commit-push and push commands through a shared resolvePushTarget helper and clearer progress notifications.
  • Improve commit list rendering and tooltip setup with safer parsing and type usage, and minor robustness fixes (e.g., explicit radix for parseInt, using window.setTimeout).
  • Introduce a reusable primary-button style and data-view-type attribute for webview bodies to support consistent styling and view-specific behavior.
  • Simplify remote-add command by reusing the new remote configuration flow used by push.

@justinkumpe justinkumpe self-assigned this Feb 13, 2026
@justinkumpe justinkumpe added the bug Something isn't working label Feb 13, 2026
@justinkumpe justinkumpe linked an issue Feb 13, 2026 that may be closed by this pull request
@sourcery-ai
Copy link

sourcery-ai bot commented Feb 13, 2026

Reviewer's Guide

Refactors the Git QuickOps webview to use a safer, programmatic DOM-building approach and a unified VS Code API bridge, while making git push behavior robust by resolving upstream/remote interactively, reusing a shared remote-add flow, and improving activation and loading behavior across views.

Sequence diagram for robust git push with remote and upstream resolution

sequenceDiagram
    actor User
    participant VSCode
    participant ExtensionCommands
    participant GitModule
    participant VSCodeUI

    User->>VSCode: Run cmdPush or cmdAddCommitPush
    VSCode->>ExtensionCommands: cmdPush / cmdAddCommitPush
    ExtensionCommands->>RepositoryContext: getGitRoot
    RepositoryContext-->>ExtensionCommands: gitRoot
    ExtensionCommands->>GitModule: getCurrentBranch gitRoot
    GitModule-->>ExtensionCommands: branch

    ExtensionCommands->>ExtensionCommands: resolvePushTarget gitRoot branch
    alt branch has upstream
        ExtensionCommands->>GitModule: getBranchUpstream gitRoot branch
        GitModule-->>ExtensionCommands: remote and upstreamBranch
        ExtensionCommands-->>ExtensionCommands: pushTarget args push and display remote upstreamBranch
    else no upstream
        ExtensionCommands->>VSCodeUI: read config gitQuickOps.defaultRemote
        ExtensionCommands->>GitModule: getRemotes gitRoot
        GitModule-->>ExtensionCommands: remotes
        alt no remotes
            ExtensionCommands->>VSCodeUI: showWarningMessage Add Remote or Cancel
            VSCodeUI-->>ExtensionCommands: user choice
            alt user cancels
                ExtensionCommands-->>ExtensionCommands: resolvePushTarget returns null
                ExtensionCommands-->>VSCode: return without pushing
            else user chooses Add Remote
                ExtensionCommands->>VSCodeUI: promptAddRemote name and url
                loop validate name and url
                    VSCodeUI-->>ExtensionCommands: user input
                    ExtensionCommands->>VSCodeUI: showErrorMessage on invalid input
                end
                alt remote exists
                    ExtensionCommands->>GitModule: execGit remote set url
                else new remote
                    ExtensionCommands->>GitModule: execGit remote add
                end
                ExtensionCommands->>VSCodeUI: showInformationMessage Remote configured
                ExtensionCommands->>GitModule: getRemotes gitRoot
                GitModule-->>ExtensionCommands: remotes updated
            end
        end

        alt defaultRemote in remotes
            ExtensionCommands-->>ExtensionCommands: selectedRemote is defaultRemote
        else multiple remotes
            ExtensionCommands->>VSCodeUI: quickPick remotes plus Add new remote
            VSCodeUI-->>ExtensionCommands: selected item
            alt user selects Add new remote
                ExtensionCommands->>VSCodeUI: promptAddRemote
                VSCodeUI-->>ExtensionCommands: remoteName or cancellation
                alt cancelled
                    ExtensionCommands-->>ExtensionCommands: resolvePushTarget returns null
                    ExtensionCommands-->>VSCode: return without pushing
                else remote created
                    ExtensionCommands-->>ExtensionCommands: selectedRemote is new remote
                end
            else user selects existing remote
                ExtensionCommands-->>ExtensionCommands: selectedRemote is chosen label
            end
        end

        ExtensionCommands->>VSCodeUI: quickPick Yes or No to set upstream
        VSCodeUI-->>ExtensionCommands: user choice
        alt user declines
            ExtensionCommands-->>ExtensionCommands: resolvePushTarget returns null
            ExtensionCommands-->>VSCode: return without pushing
        else user accepts
            ExtensionCommands-->>ExtensionCommands: pushTarget args push u remote branch and display
        end
    end

    alt pushTarget is not null
        ExtensionCommands->>VSCodeUI: withProgress Pushing to display
        VSCodeUI->>GitModule: execGit or runGitCommand gitRoot pushTarget args
        alt git push succeeds
            GitModule-->>VSCodeUI: success
            VSCodeUI-->>ExtensionCommands: progress complete
            ExtensionCommands->>VSCodeUI: showInformationMessage push success
        else git push fails
            GitModule-->>VSCodeUI: error
            VSCodeUI-->>ExtensionCommands: propagate error
            ExtensionCommands->>VSCodeUI: showErrorMessage Push failed error
        end
    end
Loading

Sequence diagram for unified webview command posting from UI elements

sequenceDiagram
    participant User
    participant WebviewDOM
    participant WebviewScript
    participant VSCodeApi
    participant WebviewProvider
    participant ExtensionLogic

    User->>WebviewDOM: Click UI element for example Stage file
    WebviewDOM->>WebviewScript: Event listener callback
    WebviewScript->>WebviewScript: Determine command stageFile and path
    WebviewScript->>VSCodeApi: postMessage command stageFile with path

    VSCodeApi->>WebviewProvider: onDidReceiveMessage
    WebviewProvider->>WebviewProvider: _handleMessage switch on data command
    WebviewProvider->>ExtensionLogic: stage file in repository

    User->>WebviewDOM: Click navigation item
    WebviewDOM->>WebviewScript: Event handler for navigateTo or navigateToView
    WebviewScript->>VSCodeApi: postMessage command navigateTo or navigateToView
    VSCodeApi->>WebviewProvider: onDidReceiveMessage
    WebviewProvider->>ExtensionLogic: perform navigation and refresh view

    User->>WebviewDOM: Submit setup form
    WebviewDOM->>WebviewScript: saveSetup handler reads commitPrefix and requireTests
    WebviewScript->>VSCodeApi: postMessage command saveSetup with config
    VSCodeApi->>WebviewProvider: onDidReceiveMessage
    WebviewProvider->>ExtensionLogic: persist configuration and update views
Loading

Flow diagram for webview renderView and view specific rendering

flowchart TD
    A_start[Start renderView data]
    A_check_data{data exists}
    A_error_no_data[Render error No data received from extension]
    A_check_error{data error field}
    A_render_error[renderError data error]
    A_switch_type{data type}
    B_repos[renderRepositories]
    C_menu[renderNavigation]
    D_setup[renderSetup]
    E_changes[renderChanges]
    F_commits[renderCommits]
    G_unknown[renderError Unknown view data type]
    H_catch_error[Catch exception and log]
    H_render_error[Render error Failed to render view]

    A_start --> A_check_data
    A_check_data -- no --> A_error_no_data --> H_end[End]
    A_check_data -- yes --> A_check_error
    A_check_error -- yes --> A_render_error --> H_end
    A_check_error -- no --> A_switch_type

    A_switch_type -- repositories --> B_repos --> H_end
    A_switch_type -- menu --> C_menu --> H_end
    A_switch_type -- setup --> D_setup --> H_end
    A_switch_type -- changes --> E_changes --> H_end
    A_switch_type -- commits --> F_commits --> H_end
    A_switch_type -- other --> G_unknown --> H_end

    A_start --> H_try[Try block]
    H_try --> A_check_data
    H_try --> H_catch_error
    H_catch_error --> H_render_error --> H_end
Loading

File-Level Changes

Change Details Files
Refactor webview rendering to use programmatic DOM creation instead of innerHTML and centralize command wiring to a single VS Code API instance.
  • Acquire VS Code API once in media/main.js and send an initial webviewReady message on DOMContentLoaded.
  • Introduce helper functions to clear and populate the #content element and to create DOM elements with classes/text.
  • Rewrite renderRepositories, renderNavigation, renderChanges, renderCommits, renderSetup, renderFileItem, and renderCommitItem to build DOM nodes instead of writing HTML strings with inline onclick handlers.
  • Remove drawCommitGraph and the generic escapeHtml helper, and replace string-based commit badge construction with object-based badge descriptions.
  • Convert command functions (selectRepo, switchRepo, etc.) to internal functions and explicitly export them by assigning to window at the end of the IIFE.
  • Add minor safety fixes such as parseInt radix and using window.setTimeout for context menu teardown.
media/main.js
Improve setup view styling with a reusable primary button class.
  • Define a .primary-button class with VS Code button theming and hover styles.
  • Update the setup form save button to use the primary-button class instead of inline styles.
media/style.css
media/main.js
Make webview initialization explicit and avoid premature refreshes.
  • Remove the immediate refresh call when resolving the webview in GitQuickOpsWebviewProvider.
  • Handle a new webviewReady message to trigger refresh once the webview script has loaded.
  • Set the view type via a data-view-type attribute on the body instead of injecting global JS variables in the HTML template.
src/webviewProvider.ts
media/main.js
Make push operations robust by resolving upstream/remote targets interactively and handling errors correctly.
  • Introduce resolvePushTarget to determine push arguments and display string, preferring an existing upstream and falling back to a remote selection flow when none is configured.
  • Add getBranchUpstream to parse the upstream remote/branch via git rev-parse and handle missing upstreams gracefully.
  • Update cmdAddCommitPush and cmdPush to use resolvePushTarget, wrap push execution in withProgress, and show information vs error messages based on success or failure.
  • Ensure both execGit and runGitCommand push calls use the dynamically resolved arguments instead of assuming a default remote/branch pair.
src/extension.ts
Add an interactive remote selection and configuration flow, and reuse it for both push and explicit remote-add commands.
  • Introduce selectRemoteForPush to choose an appropriate remote, handling no remotes, defaultRemote presence, single-remote fast path, and multi-remote selection with a QuickPick option to add a new remote.
  • Add promptAddRemote to guide the user through entering a remote name and URL with validation, handling existing remote updates via set-url and reporting configuration success.
  • Wire selectRemoteForPush into resolvePushTarget so that missing default remotes are handled interactively instead of blindly using a config value.
  • Refactor cmdRemoteAdd to delegate to promptAddRemote, reusing the same validation and UX as the push flow.
src/extension.ts
Adjust extension activation behavior to align with individual Git QuickOps views.
  • Change activationEvents from a single command-based activation to view-based activation for the repositories, menu, changes, and commits views.
package.json

Assessment against linked issues

Issue Objective Addressed Explanation
#5 Handle git push when no upstream/remote is configured (instead of failing), by detecting the condition and guiding the user to select or add a remote and set the upstream.
#5 Prevent the push command from hanging or falsely reporting success when push fails, by surfacing push errors to the user.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • In selectRemoteForPush, the QuickPick items are custom objects with a value field; consider using proper vscode.QuickPickItem typing (e.g. storing the remote name in label and checking selected.label) to avoid TypeScript type mismatches and make the API usage clearer.
  • In renderCommitItem, const author = commit.author.split(' ')[0]; will truncate multi-word names and behave oddly with leading spaces; consider using a more robust approach (e.g. using the full author string or splitting on < if you want the name portion).
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `selectRemoteForPush`, the QuickPick items are custom objects with a `value` field; consider using proper `vscode.QuickPickItem` typing (e.g. storing the remote name in `label` and checking `selected.label`) to avoid TypeScript type mismatches and make the API usage clearer.
- In `renderCommitItem`, `const author = commit.author.split(' ')[0];` will truncate multi-word names and behave oddly with leading spaces; consider using a more robust approach (e.g. using the full author string or splitting on `<` if you want the name portion).

## Individual Comments

### Comment 1
<location> `src/extension.ts:858-860` </location>
<code_context>
+    return selected.value;
+}
+
+async function promptAddRemote(gitRoot: string): Promise<string | null> {
+    const existingRemotes = await git.getRemotes(gitRoot);
+    const remoteName = await vscode.window.showInputBox({
+        prompt: 'Enter remote name',
+        placeHolder: 'origin'
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Validate `remoteName` and `remoteUrl` inputs to avoid obviously invalid values.

Right now any non-empty string is accepted for `remoteName`/`remoteUrl`, so values with leading/trailing spaces or spaces in the name can slip through and later break `git remote` commands.

Please trim both inputs and add minimal validation, e.g. reject names containing whitespace and empty URLs, and either show an error or re-prompt the user.

Suggested implementation:

```typescript
    const existingRemotes = await git.getRemotes(gitRoot);

    let remoteName: string;
    while (true) {
        const input = await vscode.window.showInputBox({
            prompt: 'Enter remote name',
            placeHolder: 'origin'
        });

        if (!input) {
            // User cancelled the input
            return null;
        }

        const trimmed = input.trim();
        if (!trimmed) {
            vscode.window.showErrorMessage('Remote name cannot be empty or whitespace.');
            continue;
        }

        if (/\s/.test(trimmed)) {
            vscode.window.showErrorMessage('Remote name cannot contain whitespace.');
            continue;
        }

        remoteName = trimmed;
        break;
    }

    let remoteUrl: string;
    while (true) {
        const input = await vscode.window.showInputBox({
            prompt: 'Enter remote URL',
            placeHolder: 'https://github.com/user/repo.git'
        });

        if (!input) {
            // User cancelled the input
            return null;
        }

        const trimmed = input.trim();
        if (!trimmed) {
            vscode.window.showErrorMessage('Remote URL cannot be empty or whitespace.');
            continue;
        }

        remoteUrl = trimmed;
        break;
    }

```

1. Ensure that any subsequent usage of `remoteName` and `remoteUrl` within `promptAddRemote` relies on these now-trimmed variables (the identifiers are unchanged, so this should be transparent).
2. If you want to prevent duplicate remote names using `existingRemotes`, you could extend the `remoteName` validation loop to check `existingRemotes.some(r => r.name === trimmed)` and show an error if it already exists.
</issue_to_address>

### Comment 2
<location> `media/main.js:671-674` </location>
<code_context>
+    }

-    window.saveSetup = function() {
+    function saveSetup() {
+        const commitPrefix = document.getElementById('commitPrefix');
+        const requireTests = document.getElementById('requireTests');
         const config = {
-            commitPrefix: document.getElementById('commitPrefix').value,
-            requireTests: document.getElementById('requireTests').value
</code_context>

<issue_to_address>
**issue (bug_risk):** Avoid silently falling back to default config values when `commitPrefix` or `requireTests` elements are missing.

Right now, if either element lookup returns `null`, you end up posting a config with `''` / `'disabled'`, overwriting the existing config with defaults without any indication to the user.

Add an explicit guard so you fail fast instead of silently resetting:

```js
if (!commitPrefix || !requireTests) {
  console.error('Setup form controls not found');
  return;
}
```

This makes DOM issues visible and prevents unintended config changes.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@justinkumpe
Copy link
Member Author

@sourcery-ai resolve

@justinkumpe
Copy link
Member Author

@sourcery-ai review

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've left some high level feedback:

  • By removing the onCommand:git-quickops.showMenu activation event and only activating on views, invoking the git-quickops.showMenu command from the command palette may no longer activate the extension; consider keeping the command-based activation if the command is meant to be callable independently of the views.
  • The push error handlers currently display Push failed: ${error}, which may stringify as [object Object]; consider normalizing the error (e.g., checking error instanceof Error ? error.message : String(error)) for clearer user-facing messages.
  • The push flow in cmdAddCommitPush and cmdPush now shares the same resolvePushTarget logic but still duplicates the progress and notification handling; consider extracting a common helper to run the push and show messages so behavior stays consistent and easier to maintain.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- By removing the `onCommand:git-quickops.showMenu` activation event and only activating on views, invoking the `git-quickops.showMenu` command from the command palette may no longer activate the extension; consider keeping the command-based activation if the command is meant to be callable independently of the views.
- The push error handlers currently display `Push failed: ${error}`, which may stringify as `[object Object]`; consider normalizing the error (e.g., checking `error instanceof Error ? error.message : String(error)`) for clearer user-facing messages.
- The push flow in `cmdAddCommitPush` and `cmdPush` now shares the same `resolvePushTarget` logic but still duplicates the progress and notification handling; consider extracting a common helper to run the push and show messages so behavior stays consistent and easier to maintain.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@justinkumpe justinkumpe added this pull request to the merge queue Feb 13, 2026
Merged via the queue into main with commit 9fe14cf Feb 13, 2026
5 checks passed
@justinkumpe justinkumpe deleted the bug/5 branch February 13, 2026 17:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug] push failing when remote not set

1 participant