func (m *Manager) dualRepoExecute(dual dualRepoFunc, j *jobs.Job) error {
	if len(j.Src) == 0 {
		return errors.New("job is missing a source repo")
	}
	if len(j.Dst) == 0 {
		return errors.New("job is missing a destination repo")
	}
	tx, err := m.db.Beginx()
	if err != nil {
		return fmt.Errorf("failed to start DB transaction, reason: '%s'", err.Error())
	}
	src, err := repo.Get(tx, j.Src)
	if err != nil {
		tx.Rollback()
		return fmt.Errorf("failed to get the source Repo entry from the DB, reason: '%s'", err.Error())
	}
	dst, err := repo.Get(tx, j.Dst)
	if err != nil {
		tx.Rollback()
		return fmt.Errorf("failed to get the destination Repo entry from the DB, reason: '%s'", err.Error())
	}
	var diff *repo.Diff
	if diff, err = dual(src, dst, j, tx); err != nil {
		tx.Rollback()
		return err
	}
	if err = tx.Commit(); err != nil {
		return fmt.Errorf("failed to commit the transaction, reason: '%s'", err.Error())
	}
	j.Results, err = diff.MarshalBinary()
	if err != nil {
		return fmt.Errorf("failed to convert Diff to binary for saving, reason: '%s'", err.Error())
	}
	return nil
}
dualRepoExecute carries out an action on two repos which generates a Diff
package manager
import (
	"errors"
	"fmt"
	"github.com/getsolus/ferryd/jobs"
	"github.com/getsolus/ferryd/repo"
	"github.com/jmoiron/sqlx"
)
type dualRepoFunc func(left, right *repo.Repo, j *jobs.Job, tx *sqlx.Tx) (d *repo.Diff, err error)