# Deduplicate shared helpers and remove dead code

`025234e`→[main](/content/gh/entireio/git-sync/commits/main/index.html)·  
  
Soph·3mo ago·10 files·+64 added/-117 removed

- Extract convert.DesiredRefs and convert.PlansToPushPlans into internal/convert to eliminate 4 copies across strategy packages  
- Remove dead PlannerDesired struct from gitproto/convert.go  
- Remove unused statsCollector.addWantsHaves and addCommands  
- Fix SortedUniqueHashes to use map[Hash]struct{} for consistency

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

## Sessions

6a10067e2e43View transcript

## Changes

10

- internal

- convert

- Aconvert.go+39

- gitproto

- Mconvert.go+1/-10

- Mfetch.go+3/-3

- strategy

- bootstrap

- Mbootstrap.go+6/-27

- incremental

- Mincremental.go+5/-26

- Mincremental_test.go+3/-2

- materialized

- Mmaterialized.go+2/-11

- Mmaterialized_test.go+2/-1

- syncer

- Mstats.go-21

- Msyncer.go+3/-16

```  
// Package convert provides shared type conversions between planner and gitproto
// types. It exists to avoid duplicating these helpers across strategy packages,
// while keeping planner and gitproto free of circular imports.
package convert

import (
	"github.com/go-git/go-git/v5/plumbing"

"github.com/soph/git-sync/internal/gitproto"
	"github.com/soph/git-sync/internal/planner"
)

// DesiredRefs converts planner desired refs to gitproto desired refs.
func DesiredRefs(desired map[plumbing.ReferenceName]planner.DesiredRef) map[plumbing.ReferenceName]gitproto.DesiredRef {
	out := make(map[plumbing.ReferenceName]gitproto.DesiredRef, len(desired))
	for k, v := range desired {
		out[k] = gitproto.DesiredRef{
			SourceRef:  v.SourceRef,
			TargetRef:  v.TargetRef,
			SourceHash: v.SourceHash,
			IsTag:      v.Kind == planner.RefKindTag,
		}
	}
	return out
}

// PlansToPushPlans converts planner BranchPlans to gitproto PushPlans.
func PlansToPushPlans(plans []planner.BranchPlan) []gitproto.PushPlan {
	out := make([]gitproto.PushPlan, len(plans))
	for i, p := range plans {
		out[i] = gitproto.PushPlan{
			TargetRef:  p.TargetRef,
			TargetHash: p.TargetHash,
			SourceHash: p.SourceHash,
			Delete:     p.Action == planner.ActionDelete,
		}
	}
	return out
}
```

Ainternal/convert/convert.go+39

```  
// PlannerDesired is the subset of planner.DesiredRef fields needed by gitproto.
// This avoids a circular import between gitproto and planner.
type PlannerDesired struct {
	SourceRef  plumbing.ReferenceName
	TargetRef  plumbing.ReferenceName
	SourceHash plumbing.Hash
	IsTag      bool
}

// ToPushCommands converts a slice of plan-like structs to PushCommands.
// ToPushCommands converts a slice of PushPlans to PushCommands.
// Used by all strategy packages to avoid copy-pasting the conversion.
func ToPushCommands(plans []PushPlan) []PushCommand {
	cmds := make([]PushCommand, 0, len(plans))
...
```

... (Other code and functions are present) ...

```  
// --- Shared helpers ---

func toGP(desired map[plumbing.ReferenceName]planner.DesiredRef) map[plumbing.ReferenceName]gitproto.DesiredRef {
	out := make(map[plumbing.ReferenceName]gitproto.DesiredRef, len(desired))
	for k, v := range desired {
		out[k] = gitproto.DesiredRef{
			SourceRef: v.SourceRef, TargetRef: v.TargetRef,
			SourceHash: v.SourceHash, IsTag: v.Kind == planner.RefKindTag,
		}
	}
	return out
}

func plansToPushPlans(plans []planner.BranchPlan) []gitproto.PushPlan {
	out := make([]gitproto.PushPlan, len(plans))
	for i, p := range plans {
		out[i] = gitproto.PushPlan{
			TargetRef: p.TargetRef, TargetHash: p.TargetHash, SourceHash: p.SourceHash,
			Delete: p.Action == planner.ActionDelete,
		}
	}
	return out
}
```

... (Additional references and code snippets) ...
