Match hoist on SourceRef so --map remappings push the right target first · Entire

Match hoist on SourceRef so --map remappings push the right target first

833b123→main·

Soph·2mo ago·3 files·+91 added/-42 removed

Cursor caught a real bug: hoistSourceHeadCommand compared cmd.Name (which PlansToPushCommands sets from p.TargetRef) against sourceHEAD the source-side symref target). Under --map (e.g., master→stable), target ref name diverges from source — the match silently fails and the bootstrap pushes branches in alphabetical order, defeating the fix on the very setups that need it most (custom branch naming on mirror).

Rework to operate on plans instead of commands, matching SourceRef against sourceHEAD. The mapped TargetRef inherits its position in the plans slice, so PlansToPushCommands emits commands in the right order. Aligns with executeBatched's orderTrunkFirst which already keys on DesiredRef.SourceRef.

Regression test pins --map master:stable + a 1:1 alpha mapping: the target server's first command must be refs/heads/stable. Without the fix, the bootstrap pushes alpha first. Unit test gains a case asserting "matches on SourceRef, hoists mapped TargetRef".

Sessions

81dd3a769cb7View transcript

Changes

3

175 unmodified lines

176
177
178
179
180
179
180
181
182
13 unmodified lines

196
197
198
200
201
199
200
201
202
203
204
203
204
205
206
206
207
208
208
209
209
210
211
212
213

175 unmodified lines

if p.OnPhase != nil {
        p.OnPhase("pushing pack")
    }
    cmds := convert.PlansToPushCommands(plans)
    cmds = hoistSourceHeadCommand(cmds, p.SourceHeadTarget)
    pushErr := p.TargetPusher.PushPack(ctx, cmds, packReader)
    _ = packReader.Close()
    if pushErr != nil {
13 unmodified lines

return result, nil
}

// hoistSourceHeadCommand moves the push command for the source's symref
// HEAD target to the front of cmds. Hosts that pick the default branch
// hoistSourceHeadPlan moves the plan whose source ref matches the
// source's symref HEAD target to the front, so the resulting push
// commands send that ref first. Hosts that pick the default branch
// from the first push on a fresh repo (GitHub, GitLab) end up with the
// right default.
func hoistSourceHeadCommand(cmds []gitproto.PushCommand, sourceHEAD plumbing.ReferenceName) []gitproto.PushCommand {
// right default. Matching is on SourceRef rather than TargetRef so
// --map remappings push the correct (mapped) target ref first.
func hoistSourceHeadPlan(plans []planner.BranchPlan, sourceHEAD plumbing.ReferenceName) []planner.BranchPlan {
    if sourceHEAD == "" {
        return cmds
    }
    return plans
}

Minternal/strategy/bootstrap/bootstrap.go+10/-9

21 unmodified lines

...

func TestHoistSourceHeadCommand(t *testing.T) {
func TestHoistSourceHeadPlan(t *testing.T) {
main := plumbing.NewBranchReferenceName("main")
master := plumbing.NewBranchReferenceName("master")
alpha := plumbing.NewBranchReferenceName("alpha")
stable := plumbing.NewBranchReferenceName("stable")

cmd := func(name plumbing.ReferenceName) gitproto.PushCommand {
    return gitproto.PushCommand{Name: name}
}
plan := func(source, target plumbing.ReferenceName) planner.BranchPlan {
    return planner.BranchPlan{SourceRef: source, TargetRef: target}
}

tests := []struct {
    name   string
    cmds   []gitproto.PushCommand
    head   plumbing.ReferenceName
    wanted []plumbing.ReferenceName
    name           string
    plans          []planner.BranchPlan
    head           plumbing.ReferenceName
    wantTargetRefs []plumbing.ReferenceName
}{
    {
        name:   "hoists matching command to front",
        cmds:   []gitproto.PushCommand{cmd(alpha), cmd(main), cmd(master)},
        head:   main,
        wanted: []plumbing.ReferenceName{main, alpha, master},
    name:           "hoists matching plan to front",
    plans:          []planner.BranchPlan{plan(alpha, alpha), plan(main, main), plan(master, master)},
    head:           main,
    wantTargetRefs: []plumbing.ReferenceName{main, alpha, master},
    },
    ...
    }
for _, tt := range tests {
    t.Run(tt.name, func(t *testing.T) {
        got := hoistSourceHeadCommand(tt.cmds, tt.head)
    ... // remaining test code
    }
}

Minternal/syncer/integration_test.go+47