-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
What? - Add default cobra CLI project structure - Implement bare minimum logic to merge repos into the current working directory using: - https://jeffkreeftmeijer.com/git-combine/ - https://alexharv074.github.io/puppet/2017/10/04/merge-a-git-repository-and-its-history-into-a-subdirectory-of-a-second-git-repository.html Notes - Does not currently support merging local repos, or remotes pulled from a non SSH remote. - Documentation has not been added yet
- Loading branch information
1 parent
2ae157c
commit 150c88f
Showing
7 changed files
with
225 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package cmd | ||
|
||
import ( | ||
"os" | ||
|
||
"github.com/spf13/cobra" | ||
) | ||
|
||
var rootCmd = &cobra.Command{ | ||
Use: "many2mono", | ||
Short: "TBD", | ||
} | ||
|
||
func Execute() { | ||
err := rootCmd.Execute() | ||
if err != nil { | ||
os.Exit(1) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package cmd | ||
|
||
import ( | ||
"log" | ||
"strings" | ||
|
||
"github.com/oliverhohn/many2mono/helper" | ||
"github.com/oliverhohn/many2mono/model" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
// CLI flags | ||
var defaultBranch string | ||
var dryRun bool | ||
|
||
var runCmd = &cobra.Command{ | ||
Use: "run", | ||
Short: "TBD", | ||
Args: cobra.MatchAll(cobra.MinimumNArgs(1)), | ||
Run: func(cmd *cobra.Command, args []string) { | ||
repos := []*model.Repo{} | ||
for _, path := range args { | ||
r, err := model.NewRepo(path) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
repos = append(repos, r) | ||
} | ||
|
||
if duplicateRepos := findDuplicateRepoNames(repos); len(duplicateRepos) > 0 { | ||
log.Fatalf("Cannot merge as repos with duplicate names found: %s", strings.Join(duplicateRepos, ", ")) | ||
} | ||
|
||
for _, r := range repos { | ||
helper.FetchRemote(r, dryRun) | ||
helper.MergeHistories(r, defaultBranch, dryRun) | ||
helper.PrefixFiles(r, defaultBranch, dryRun) | ||
helper.CommitChange(r, dryRun) | ||
helper.RemoveRemote(r, dryRun) | ||
} | ||
}, | ||
} | ||
|
||
func findDuplicateRepoNames(repos []*model.Repo) []string { | ||
reposByName := map[string][]*model.Repo{} | ||
|
||
for _, r := range repos { | ||
if _, ok := reposByName[r.NameWithoutOrg()]; !ok { | ||
reposByName[r.NameWithoutOrg()] = []*model.Repo{} | ||
} | ||
|
||
reposByName[r.NameWithoutOrg()] = append(reposByName[r.NameWithoutOrg()], r) | ||
} | ||
|
||
ret := []string{} | ||
for name, repos := range reposByName { | ||
if len(repos) > 1 { | ||
ret = append(ret, name) | ||
} | ||
} | ||
|
||
return ret | ||
} | ||
|
||
func init() { | ||
rootCmd.AddCommand(runCmd) | ||
|
||
runCmd.Flags().StringVarP(&defaultBranch, "branch", "b", "main", "TBD") | ||
|
||
runCmd.Flags().BoolVar(&dryRun, "dry-run", false, "TBD") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,13 @@ | ||
module github.com/oliverhohn/many2mono | ||
|
||
go 1.20 | ||
|
||
require ( | ||
github.com/spf13/cobra v1.7.0 | ||
github.com/whilp/git-urls v1.0.0 | ||
) | ||
|
||
require ( | ||
github.com/inconshreveable/mousetrap v1.1.0 // indirect | ||
github.com/spf13/pflag v1.0.5 // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= | ||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= | ||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= | ||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= | ||
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= | ||
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= | ||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= | ||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= | ||
github.com/whilp/git-urls v1.0.0 h1:95f6UMWN5FKW71ECsXRUd3FVYiXdrE7aX4NZKcPmIjU= | ||
github.com/whilp/git-urls v1.0.0/go.mod h1:J16SAmobsqc3Qcy98brfl5f5+e0clUvg1krgwk/qCfE= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
package helper | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"os/exec" | ||
|
||
"github.com/oliverhohn/many2mono/model" | ||
) | ||
|
||
func FetchRemote(r *model.Repo, dryRun bool) { | ||
cmd := exec.Command("git", "remote", "add", "-f", r.Name(), r.URL.String()) | ||
|
||
err := runCommand(cmd, dryRun) | ||
if err != nil { | ||
log.Fatalf("unable to fetch remote %s, due to: %v", r.URL.String(), err) | ||
} | ||
} | ||
|
||
func RemoveRemote(r *model.Repo, dryRun bool) { | ||
cmd := exec.Command("git", "remote", "remove", r.Name()) | ||
|
||
err := runCommand(cmd, dryRun) | ||
if err != nil { | ||
log.Fatalf("unable to remove remote %s, due to: %v", r.Name(), err) | ||
} | ||
} | ||
|
||
func MergeHistories(r *model.Repo, branch string, dryRun bool) { | ||
remote := fmt.Sprintf("%s/%s", r.Name(), branch) | ||
cmd := exec.Command("git", "merge", "--allow-unrelated-histories", "--strategy=ours", "--no-commit", remote) | ||
|
||
err := runCommand(cmd, dryRun) | ||
if err != nil { | ||
log.Fatalf("unable to merge histories from remote %s, due to: %v", remote, err) | ||
} | ||
} | ||
|
||
func PrefixFiles(r *model.Repo, branch string, dryRun bool) { | ||
remote := fmt.Sprintf("%s/%s", r.Name(), branch) | ||
cmd := exec.Command("git", "read-tree", fmt.Sprintf("--prefix=%s", r.NameWithoutOrg()), "-u", remote) | ||
|
||
err := runCommand(cmd, dryRun) | ||
if err != nil { | ||
log.Fatalf("unable to prefix files from remote %s, due to: %v", remote, err) | ||
} | ||
} | ||
|
||
func CommitChange(r *model.Repo, dryRun bool) { | ||
cmd := exec.Command("git", "commit", fmt.Sprintf("--message=\"Merged %s\"", r.Name())) | ||
|
||
err := runCommand(cmd, dryRun) | ||
if err != nil { | ||
log.Fatalf("unable to commit merge from remote %s, due to: %v", r.Name(), err) | ||
} | ||
} | ||
|
||
func runCommand(cmd *exec.Cmd, dryRun bool) error { | ||
fmt.Printf("Running %s\n", cmd.String()) | ||
|
||
if !dryRun { | ||
out, err := cmd.CombinedOutput() | ||
if err != nil { | ||
return fmt.Errorf("%v\ntrace:%s", err, out) | ||
} | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
package main | ||
|
||
import "fmt" | ||
import "github.com/oliverhohn/many2mono/cmd" | ||
|
||
func main() { | ||
fmt.Println("Hello, world!") | ||
cmd.Execute() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package model | ||
|
||
import ( | ||
"fmt" | ||
"net/url" | ||
"path/filepath" | ||
"strings" | ||
|
||
gitURLs "github.com/whilp/git-urls" | ||
) | ||
|
||
type Repo struct { | ||
URL *url.URL | ||
} | ||
|
||
func NewRepo(path string) (*Repo, error) { | ||
u, err := gitURLs.Parse(path) | ||
if err != nil { | ||
return nil, fmt.Errorf("unable to initialize repo from %s, due to: %w", path, err) | ||
} | ||
|
||
if u.Scheme != "ssh" { | ||
return nil, fmt.Errorf("%s is an unsupported scheme for repo URLs, only ssh is supported", u.Scheme) | ||
} | ||
|
||
return &Repo{URL: u}, nil | ||
} | ||
|
||
func (r *Repo) Name() string { | ||
ext := filepath.Ext(r.URL.Path) | ||
|
||
name := strings.TrimSuffix(r.URL.Path, ext) | ||
|
||
return strings.TrimSpace(name) | ||
} | ||
|
||
func (r *Repo) NameWithoutOrg() string { | ||
nameComponents := strings.Split(r.Name(), "/") | ||
|
||
return nameComponents[len(nameComponents)-1] | ||
} |