# fix(trail): fetch existing remote branch before create

`1fd4f9f`→[main](/content/gh/entireio/cli/commits/main/index.html)·

dipree·4w ago·1 file·+35 added/-11 removed

Address PR review feedback:
- validate branch names before trail create reaches git subcommands
- reject leading-dash branch names before invoking git
- when a requested branch is missing locally but already exists on origin,
fetch it instead of creating a new local branch at HEAD
- make the non-fast-forward hint refer to the target branch

## Sessions

b73b63b3e546View transcript

[?\
Fix Trail Creation Branch Handling and ValidationPi·Opus 4.8·1 step](/content/gh/entireio/cli/session/019ed956-c81a-74d1-9437-9fb289e1410f#timeline-b73b63b3e546/index.html)

## Changes

1

- cmd/entire/cli

- Mtrail_cmd.go+35/-11

```
634 unmodified lines

635
636
637
638
639
640
641
642
643
644
645
646
639
640
647
648
649
650
651
652
653
654
655
656
657
658
659
642
643
660
661
662
2 unmodified lines

665
666
667
652
653
654
655
656
657
668
669
660
670
671
672
673
732 unmodified lines

1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425

634 unmodified lines

localBranchCreated := false
	var remoteBranchPushed bool

// Don't delete a remote branch we didn't create during cleanup.
	existedOnOrigin, existErr := branchExistsOnOrigin(branch)
	if existErr != nil {
		fmt.Fprintf(errW, "Warning: could not check whether branch %s already exists on origin: %v\n", branch, existErr)
		existedOnOrigin = true
	}

if needsCreation {
		if err := createBranch(repo, branch); err != nil {
			return fmt.Errorf("failed to create branch %q: %w", branch, err)
		}
		if existedOnOrigin {
			if err := fetchBranchFromOrigin(branch); err != nil {
				return fmt.Errorf("failed to fetch branch %q from origin: %w", branch, err)
			}
			localBranchCreated = true
			fmt.Fprintf(w, "Fetched branch %s from origin\n", branch)
		} else {
			if err := createBranch(repo, branch); err != nil {
				return fmt.Errorf("failed to create branch %q: %w", branch, err)
			}
			localBranchCreated = true
			fmt.Fprintf(w, "Created branch %s\n", branch)
		}
		localBranchCreated = true
		fmt.Fprintf(w, "Created branch %s\n", branch)
	} else if currentBranch != branch {
		fmt.Fprintf(w, "Note: trail will be created for branch %q (not the current branch)\n", branch)
	}
2 unmodified lines

// deliver it before creating the trail rather than letting the server
	// backfill it at the base tip.
	{
		// Don't delete a remote branch we didn't create during cleanup.
		existedOnOrigin, existErr := branchExistsOnOrigin(branch)
		if existErr != nil {
			fmt.Fprintf(errW, "Warning: could not check whether branch %s already exists on origin: %v\n", branch, existErr)
			existedOnOrigin = true
		}
		if err := pushBranchToOrigin(branch); err != nil {
			cleanupCreatedTrailBranch(repo, branch, localBranchCreated, false, errW)
			return fmt.Errorf("failed to push branch %q to origin: %w\nhint: the trail was not created because its branch could not be delivered to the remote.\n  - if this is an auth error, link your GitHub account and retry\n  - if this is a non-fast-forward, update your base (git fetch && git rebase) and retry", branch, err)
		}
		remoteBranchPushed = !existedOnOrigin
		fmt.Fprintf(w, "Pushed branch %s to origin\n", branch)
732 unmodified lines

}
}

func fetchBranchFromOrigin(branchName string) error {
	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
	defer cancel()
	if err := ValidateBranchName(ctx, branchName); err != nil {
		return err
	}
	refspec := fmt.Sprintf("refs/heads/%%s:refs/heads/%%s", branchName, branchName)
	cmd := exec.CommandContext(ctx, "git", "fetch", "--no-tags", "origin", refspec)
	if output, err := cmd.CombinedOutput(); err != nil {
		return fmt.Errorf("%s: %w", strings.TrimSpace(string(output)), err)
	}
	return nil
}

// pushBranchToOrigin pushes a branch to the origin remote.
func pushBranchToOrigin(branchName string) error {
	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
