Skip to content

Add --fetch flag to delete command#103

Open
alexrinass wants to merge 4 commits intomainfrom
feature/88-fetch-before-delete
Open

Add --fetch flag to delete command#103
alexrinass wants to merge 4 commits intomainfrom
feature/88-fetch-before-delete

Conversation

@alexrinass
Copy link
Copy Markdown
Contributor

@alexrinass alexrinass commented Apr 7, 2026

Adds --fetch/--no-fetch flags to the delete command so it can fetch from remote before checking merge status, solving the common case where branches are merged remotely (e.g., via GitHub PR) but Git thinks they have unmerged changes.

The implementation fetches both the parent and topic branch, fast-forwards the local parent from its remote-tracking counterpart so git branch -d correctly detects remote merges, and checks whether the local topic branch is in sync with remote (aborting if behind, unless --force). This mirrors the finish command's fetch behavior. Config key gitflow.<type>.delete.fetch (default false), RemoteExists guard, and non-fatal fetch errors. Changes touch cmd/delete.go, cmd/topicbranch.go, cmd/shorthand.go, internal/git/repo.go, manpages, and add 4 new tests.

Resolves #88

Remarks

  • Default is false (no fetch), consistent with start; users opt in via flag or config
  • After fetch, the parent branch is fast-forwarded so git branch -d can detect remote merges
  • Local topic branch sync is checked after fetch — aborts if behind remote (unless --force)
  • The shorthand git flow delete also supports the new flags

Review focus:

  • cmd/delete.go:94-144 - fetch, fast-forward, and sync check logic
  • internal/git/repo.go - new MergeFFOnly helper

When branches are merged remotely (e.g., via GitHub PR), the local
repo doesn't know about it. Without --fetch, deleting such a branch
fails because Git thinks it has unmerged changes. The --fetch flag
updates local refs before the merge check, solving this without
needing --force.

Supports CLI flags (--fetch/--no-fetch), config key
(gitflow.<type>.delete.fetch), and silently skips fetch when no
remote is configured.

Resolves #88
Add --fetch/--no-fetch options to the delete manpage with examples
and configuration reference. Add gitflow.<type>.delete.fetch config
key to the configuration manpage.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds optional remote fetching to git flow <type> delete so users can update refs before deletion checks, aiming to improve deletion of branches merged via remote PR workflows.

Changes:

  • Adds --fetch/--no-fetch flags (tri-state via *bool) to topic-branch delete and shorthand delete commands.
  • Implements gitflow.<type>.delete.fetch configuration support and performs a guarded, non-fatal fetch before delete.
  • Updates delete/config manpages and adds 4 new delete tests for flag/config/override/no-remote cases.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
cmd/delete.go Adds fetch resolution + guarded fetch execution before performing deletion.
cmd/topicbranch.go Wires new delete flags into the per-branch-type delete subcommand.
cmd/shorthand.go Wires new delete flags into git flow delete shorthand command.
docs/gitflow-config.5.md Documents the new gitflow.<type>.delete.fetch config key.
docs/git-flow-delete.1.md Documents new CLI flags and provides examples/config snippet.
test/cmd/delete_test.go Adds tests covering flag/config/override/no-remote behavior for fetch.

Comment thread cmd/delete.go
Comment on lines +94 to +99
if shouldFetch && git.RemoteExists(cfg.Remote) {
fmt.Printf("Fetching from remote '%s'...\n", cfg.Remote)
if err := git.Fetch(cfg.Remote); err != nil {
fmt.Fprintf(os.Stderr, "Warning: %v\n", err)
}
}
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

The current --fetch implementation only runs git fetch <remote>, which updates remote-tracking refs but does not update the local base branch (e.g., develop). Since deletion still relies on git branch -d/-D (merge check against local refs), fetching alone will not change whether Git considers the branch “fully merged”, so this likely won’t resolve the remotely-merged-PR scenario described in the PR/Issue #88.

To make --fetch achieve the intended behavior, consider checking merge status against the remote-tracking base (e.g., refs/remotes/<remote>/<parent>) after fetching and, when it’s merged there, proceed with a safe force-delete (or an explicit, well-documented alternative).

Copilot uses AI. Check for mistakes.
Comment thread test/cmd/delete_test.go
Comment on lines +884 to +891
// TestDeleteFeatureWithFetchFlag tests that the --fetch flag fetches before deleting.
// Steps:
// 1. Sets up a test repository with remote (includes git-flow init)
// 2. Starts a feature branch
// 3. Runs 'git flow feature delete my-feature --fetch'
// 4. Verifies output contains "Fetching from remote"
// 5. Verifies branch is deleted
func TestDeleteFeatureWithFetchFlag(t *testing.T) {
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

These new tests assert that the output contains a fetch message, but they don’t set up the failure mode from Issue #88 (branch merged into the remote base branch while the local base is behind). As written, they won’t catch regressions where fetch runs but deletion still fails without --force in the real-world remotely-merged-PR scenario.

Consider adding a test that: (1) pushes the base branch to a remote, (2) merges the topic branch into the remote base via a second clone, (3) leaves the original repo’s local base behind, and (4) verifies delete --fetch succeeds without --force where delete (without fetch) fails.

Copilot generated this review using guidance from repository custom instructions.
Comment thread docs/gitflow-config.5.md
Comment on lines +458 to +462
**gitflow.*type*.delete.fetch**
: Fetch from remote before deleting a topic branch. When enabled, updates local refs so that Git can correctly detect whether the branch has been merged remotely (e.g., via a GitHub PR merge), avoiding the need for `--force`.
: *Type*: boolean
: *Default*: false

Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

A new configuration key gitflow.<type>.delete.fetch is introduced here, but it’s not documented in CONFIGURATION.md (which appears to be the central config reference). Please add the same key there to keep the two configuration docs in sync.

Copilot uses AI. Check for mistakes.
Comment thread docs/git-flow-delete.1.md
Comment on lines +76 to +81
### Fetch Before Delete

Delete a remotely-merged branch without needing `--force`:
```bash
git flow feature delete my-feature --fetch
```
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

The docs claim --fetch lets users “Delete a remotely-merged branch without needing --force”. With the current implementation only doing git fetch before git branch -d, this may not be true because fetching doesn’t update the local base branch used by Git’s “fully merged” check. Either adjust the implementation to match this behavior, or reword the documentation to set correct expectations (e.g., that users may still need to update/fast-forward their base branch).

Copilot uses AI. Check for mistakes.
The --fetch flag only ran git fetch, which updates remote-tracking refs
but not the local parent branch. This meant git branch -d still could
not detect branches merged remotely (e.g., via GitHub PR). Now --fetch
also fetches the topic branch, fast-forwards the parent branch from its
remote-tracking counterpart, and checks if the local topic branch is in
sync with remote (aborting if behind, unless --force is given).

- Fetch both parent and topic branch individually
- Fast-forward parent after checkout so git branch -d works
- Add CompareBranchWithRemote sync check (matching finish behavior)
- Add MergeFFOnly helper to internal/git/repo.go
- Document delete.fetch in CONFIGURATION.md
- Clarify manpage description for --fetch behavior
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

cmd/delete.go:38

  • executeDelete loads config but doesn’t enforce that git-flow is initialized. Most other commands explicitly call config.IsInitialized() and return errors.NotInitializedError{} when false (e.g., cmd/start.go:31-38, cmd/list.go:29-36). Adding the same guard here would prevent delete from silently running with DefaultConfig() in uninitialized repos.
func executeDelete(branchType string, name string, force *bool, remote *bool, fetch *bool) error {
	// Load configuration
	cfg, err := config.LoadConfig()
	if err != nil {
		return &errors.GitError{Operation: "load configuration", Err: err}
	}

	// Get branch configuration
	branchConfig, ok := cfg.Branches[branchType]
	if !ok {
		return &errors.InvalidBranchTypeError{BranchType: branchType}

Comment thread cmd/shorthand.go Outdated
Comment on lines +55 to +62
var fetch *bool
if cmd.Flags().Changed("fetch") {
f, _ := cmd.Flags().GetBool("fetch")
fetch = &f
} else if cmd.Flags().Changed("no-fetch") {
f := false
fetch = &f
}
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

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

The fetch/no-fetch parsing duplicates the existing getBoolFlag(positive, negative) helper used in cmd/topicbranch.go. Consider reusing that helper here as well so flag-to-*bool conversion stays consistent across commands and is easier to maintain.

Suggested change
var fetch *bool
if cmd.Flags().Changed("fetch") {
f, _ := cmd.Flags().GetBool("fetch")
fetch = &f
} else if cmd.Flags().Changed("no-fetch") {
f := false
fetch = &f
}
fetch := getBoolFlag(cmd, "fetch", "no-fetch")

Copilot uses AI. Check for mistakes.
Comment thread internal/git/repo.go Outdated
cmd := exec.Command("git", "merge", "--ff-only", branch)
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("fast-forward merge failed: %s", strings.TrimSpace(string(output)))
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

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

MergeFFOnly drops useful context by not including the branch name and not wrapping the underlying err. Consider returning an error that includes the branch argument and wraps err (while still attaching trimmed command output) to improve diagnostics and allow callers to inspect the cause.

Suggested change
return fmt.Errorf("fast-forward merge failed: %s", strings.TrimSpace(string(output)))
outputStr := strings.TrimSpace(string(output))
return fmt.Errorf("fast-forward merge failed for branch %q: %w: %s", branch, err, outputStr)

Copilot uses AI. Check for mistakes.
Use the existing getBoolFlag helper in shorthand delete command
instead of duplicating flag-to-*bool conversion. Also include
branch name and wrap error in MergeFFOnly for better diagnostics.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.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.

Support fetch before delete

2 participants