# How detectBundledLocalDefaultCommits Prevents Silent Data Loss on Rebase

> Learn how detectBundledLocalDefaultCommits prevents silent data loss during Git rebases by blocking merges of unpushed commits, safeguarding your work.

- Repository: [Kun Chen/no-mistakes](https://github.com/kunchenguid/no-mistakes)
- Tags: internals
- Published: 2026-07-03

---

**The `detectBundledLocalDefaultCommits` function in the `no-mistakes` repository blocks rebases when a feature branch contains unpushed commits from the local default branch, preventing unrelated work from being silently merged into pull requests.**

The `no-mistakes` tool provides a robust CI/CD pipeline that guards against common Git workflow errors. When developers create feature branches from their local default branch instead of the remote, they risk bundling unpushed work into subsequent rebases. The `detectBundledLocalDefaultCommits` step specifically targets this vulnerability by analyzing branch ancestry before any rebase operation proceeds.

## The Risk of Bundled Local Default Commits

When a contributor branches from `main` instead of `origin/main`, any unpushed commits on their local default branch become embedded in the feature branch's history. During a later rebase onto `origin/main`, Git silently incorporates these commits into the pull request. This **silent data loss** occurs when another developer merges the PR, effectively claiming authorship of work that was never intended for review.

## How detectBundledLocalDefaultCommits Blocks Unsafe Rebases

The detection logic in [`internal/pipeline/steps/rebase.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/steps/rebase.go) employs a six-step verification process to identify bundled commits before they contaminate the rebase.

### 1. Scope Validation

The function first verifies it is operating on a non-default branch. It returns immediately if the current branch is empty or matches the default branch name (e.g., `main` or `master`).

### 2. Local and Remote State Comparison

The implementation queries the current tip of the local default branch using `git rev-parse refs/heads/<default>`. It simultaneously confirms the existence of the remote default branch (`origin/<default>`) to establish a comparison baseline.

### 3. Ancestry Verification

The function performs two critical reachability tests using an `isAncestor` helper:
- Verifies the local default tip is **not** already reachable from the remote default tip
- Confirms the current branch actually contains the local default tip (`isAncestor(localTip, HEAD)`)

### 4. Unpushed Commit Enumeration

When the local tip diverges from the remote, the function lists all commits between the remote tip and local tip, including the specific files each commit modifies.

### 5. Blocking Outcome Generation

If unpushed commits are detected, the function constructs a `StepOutcome` struct with `NeedsApproval: true` and `AutoFixable: false`. This halts the pipeline and requires manual intervention.

## Implementation in the Rebase Pipeline

In [`internal/pipeline/steps/rebase.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/steps/rebase.go), the detection occurs at lines 74-81, immediately before any rebase logic executes. The function signature accepts the branch name and default branch identifier:

```go
func detectBundledLocalDefaultCommits(ctx context.Context, stepCtx *StepContext, branch, defaultBranch string) *StepOutcome

```

The Git operations leverage wrappers from the `internal/git` package, including `git.Run` and `git.DiffNameOnly`, to execute repository queries safely.

## Practical Usage Example

When the detector identifies bundled commits, it produces actionable output for the developer:

```go
// Example: Handling a blocked rebase outcome
outcome := detectBundledLocalDefaultCommits(ctx, stepCtx, "feature-xyz", "main")
if outcome != nil {
    fmt.Println("Rebase blocked:", outcome.Findings)
    return outcome, nil
}

```

Typical console output warns the user of specific bundled commits:

```text
⚠️  branch bundles 3 unpushed main commit(s)
- a1b2c3d Fix typo in docs
- d4e5f6g Add experimental API
- h7i8j9k Refactor helper functions

Push main to origin, or rebase your branch onto origin/main before gating.

```

This explicit enumeration prevents the silent incorporation of `Fix typo in docs` or `Add experimental API` into the pull request.

## Summary

- **`detectBundledLocalDefaultCommits`** in [`internal/pipeline/steps/rebase.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/steps/rebase.go) prevents silent data loss by intercepting rebases that would bundle unpushed default branch commits
- The function verifies **ancestry relationships** between local and remote default branches before allowing rebase operations to proceed
- It generates a **blocking `StepOutcome`** with `NeedsApproval: true` when unpushed commits are detected, forcing manual resolution
- The detection integrates directly into the rebase pipeline at lines 74-81, ensuring early failure before any destructive Git operations occur
- Console output explicitly lists affected commits and files, directing developers to push their default branch or rebase onto the remote default first

## Frequently Asked Questions

### What triggers the detectBundledLocalDefaultCommits check?

The check triggers when processing a non-default branch that contains commits from the local default branch (e.g., `main`) that do not exist on the remote default (`origin/main`). Specifically, when `isAncestor(localTip, HEAD)` returns true but `isAncestor(localTip, remoteTip)` returns false, the function identifies bundled unpushed work.

### How does the function differ from standard Git pre-rebase hooks?

Unlike client-side hooks that rely on local configuration, `detectBundledLocalDefaultCommits` operates within the `no-mistakes` pipeline step context, providing consistent enforcement across all contributors. It performs granular ancestry checks using `git rev-parse` and `isAncestor` validation rather than relying solely on ref comparisons, offering precise detection of commit reachability.

### Can the rebase proceed automatically if bundled commits are detected?

No. When bundled commits are found, the function returns a `StepOutcome` with `AutoFixable: false` and `NeedsApproval: true`. This design requires explicit human approval to proceed, ensuring developers consciously review the unpushed commits before they can be incorporated into the rebase operation.

### Where does the detection logic reside in the no-mistakes codebase?

The core implementation lives in [`internal/pipeline/steps/rebase.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/steps/rebase.go) at lines 84-112, with the invocation site at lines 74-81. Supporting Git operations are abstracted in the `internal/git` package, which provides the `git.Run` and `git.DiffNameOnly` wrappers used for commit enumeration and file diffing.