Skip to content

Commit

Permalink
feat: support moving repositories
Browse files Browse the repository at this point in the history
  • Loading branch information
zbiljic committed Jan 1, 2024
1 parent ca22a0f commit a2e7663
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 4 deletions.
12 changes: 12 additions & 0 deletions cmd/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package cmd

import "fmt"

type GitRepositoryMovedError struct {
OldURL string
NewURL string
}

func (e *GitRepositoryMovedError) Error() string {
return fmt.Sprintf("repository moved to: %s", e.NewURL)
}
8 changes: 8 additions & 0 deletions cmd/git-run.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ func gitRunUpdate(ctx context.Context, repoPath string) error {
return nil
case errors.Is(err, ErrGitRepositoryProtected):
return nil
default:
switch v := err.(type) {
case *GitRepositoryMovedError:
if err1 := gitMove(ctx, repoPath, v.OldURL, v.NewURL); err1 != nil {
return err
}
return nil
}
}

return err
Expand Down
149 changes: 146 additions & 3 deletions cmd/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ import (
"fmt"
"net/http"
"net/url"
"os"
"os/exec"
"path/filepath"
"strings"
"time"

"github.com/cenkalti/backoff/v4"
"github.com/go-git/go-billy/v5/osfs"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing"
Expand Down Expand Up @@ -87,6 +89,17 @@ func gitRepoProjectInfo(repo *git.Repository) (string, string, *plumbing.Referen
return id, remoteURL.String(), headRef, nil
}

func gitRemoteURLProjectID(repoURL string) (string, error) {
remoteURL, err := giturls.Parse(repoURL)
if err != nil {
return "", err
}

id := filepath.Join(remoteURL.Host, remoteURL.Path)

return id, nil
}

func gitRemoteConfigURL(repoPath string) (*url.URL, error) {
repo, err := git.PlainOpen(repoPath)
if err != nil {
Expand Down Expand Up @@ -380,7 +393,7 @@ func gitResetHead(ctx context.Context, repoPath string) error {
}

func gitUpdateDefaultBranch(ctx context.Context, repoPath string) error {
_, _, headRef, err := gitProjectInfo(repoPath)
_, remoteURL, headRef, err := gitProjectInfo(repoPath)
if err != nil {
return err
}
Expand Down Expand Up @@ -437,6 +450,7 @@ func gitUpdateDefaultBranch(ctx context.Context, repoPath string) error {
var urlError *url.Error
if errors.As(err, &urlError) {
prefixPrinter.Printfln("moved: %s", urlError.URL)
return &GitRepositoryMovedError{OldURL: remoteURL, NewURL: urlError.URL}
} else {
prefixPrinter.WithMessageStyle(&pterm.ThemeDefault.ErrorMessageStyle).Println(err.Error())
}
Expand Down Expand Up @@ -486,7 +500,7 @@ func gitUpdateDefaultBranch(ctx context.Context, repoPath string) error {
}

func gitResetDefaultBranch(ctx context.Context, repoPath string) error {
_, _, headRef, err := gitProjectInfo(repoPath)
_, remoteURL, headRef, err := gitProjectInfo(repoPath)
if err != nil {
return err
}
Expand Down Expand Up @@ -543,6 +557,7 @@ func gitResetDefaultBranch(ctx context.Context, repoPath string) error {
var urlError *url.Error
if errors.As(err, &urlError) {
prefixPrinter.Printfln("moved: %s", urlError.URL)
return &GitRepositoryMovedError{OldURL: remoteURL, NewURL: urlError.URL}
} else {
prefixPrinter.WithMessageStyle(&pterm.ThemeDefault.ErrorMessageStyle).Println(err.Error())
}
Expand Down Expand Up @@ -871,7 +886,7 @@ func gitCheckAndPull(ctx context.Context, repoPath string) error {
}

func gitIsRemoteUpToDate(ctx context.Context, repoPath string) (bool, error) {
_, _, headRef, err := gitProjectInfo(repoPath)
_, remoteURL, headRef, err := gitProjectInfo(repoPath)
if err != nil {
return false, err
}
Expand Down Expand Up @@ -928,6 +943,7 @@ func gitIsRemoteUpToDate(ctx context.Context, repoPath string) (bool, error) {
var urlError *url.Error
if errors.As(err, &urlError) {
prefixPrinter.WithMessageStyle(&pterm.ThemeDefault.WarningMessageStyle).Printfln("moved: %s", urlError.URL)
return false, &GitRepositoryMovedError{OldURL: remoteURL, NewURL: urlError.URL}
} else {
prefixPrinter.WithMessageStyle(&pterm.ThemeDefault.ErrorMessageStyle).Println(err.Error())
}
Expand Down Expand Up @@ -1209,3 +1225,130 @@ func gitIsDiffFileMode(ctx context.Context, repoPath string) (bool, error) {

return false, nil
}

func gitMove(ctx context.Context, repoPath, oldURL, newURL string) error {
// complicated update locking
if isUpdateMutexLocked, ok := ctx.Value(ctxKeyIsUpdateMutexLocked{}).(*abool.AtomicBool); ok {
if isUpdateMutexLocked.IsNotSet() {
updateMutex.Lock()
isUpdateMutexLocked.Set()
}
} else {
// simple
updateMutex.Lock()
}
if shouldUpdateMutexUnlock, ok := ctx.Value(ctxKeyShouldUpdateMutexUnlock{}).(bool); ok {
if shouldUpdateMutexUnlock {
defer updateMutex.Unlock()
}
} else {
// simple
defer updateMutex.Unlock()
}

printProjectInfoContext(ctx)

dryRun, _ := ctx.Value(ctxKeyDryRun{}).(bool)

prefixPrinter := ptermInfoWithPrefixText("moving")

prefixPrinter.Printf("from '%s' to '%s'", oldURL, newURL)

pterm.Println()

prefixPrinter.Print()

if dryRun {
ptermSuccessMessageStyle.Println("dry-run")
return nil
}

oldID, err := gitRemoteURLProjectID(oldURL)
if err != nil {
ptermErrorMessageStyle.Println(err.Error())
return err
}

newID, err := gitRemoteURLProjectID(newURL)
if err != nil {
ptermErrorMessageStyle.Println(err.Error())
return err
}

if !strings.HasSuffix(repoPath, oldID) {
err = fmt.Errorf("unexpected repository path: %s", repoPath)
ptermErrorMessageStyle.Println(err.Error())
return err
}

commonRepoPath := strings.TrimSuffix(repoPath, oldID)

fs := osfs.New(commonRepoPath)

newRepoPath := filepath.Join(commonRepoPath, newID)

// check if destination exists
if _, err := fs.Stat(newID); err != nil {
if os.IsExist(err) {
ptermErrorMessageStyle.Println(err.Error())
return err
}
} else {
ptermWarningMessageStyle.Printfln("already exists: %s", newRepoPath)

// just remove from old repo path
err = os.RemoveAll(repoPath)
if err != nil {
return err
}

return nil
}

err = fs.MkdirAll(filepath.Dir(newID), os.ModePerm)
if err != nil {
ptermErrorMessageStyle.Println(err.Error())
return err
}

err = fs.Rename(oldID, newID)
if err != nil {
ptermErrorMessageStyle.Println(err.Error())
return err
}

newRepo, err := git.PlainOpen(newRepoPath)
if err != nil {
ptermErrorMessageStyle.Println(err.Error())
return err
}

config, err := newRepo.Config()
if err != nil {
ptermErrorMessageStyle.Println(err.Error())
return err
}

remote, ok := config.Remotes[git.DefaultRemoteName]
if !ok {
err = fmt.Errorf("missing remote: %s", git.DefaultRemoteName)
ptermErrorMessageStyle.Println(err.Error())
return err
}

if repoURL := remote.URLs[0]; repoURL != "" {
// update to new URL
remote.URLs[0] = newURL
}

// save updated config
err = newRepo.SetConfig(config)
if err != nil {
ptermErrorMessageStyle.Println(err.Error())
return err
}

ptermSuccessMessageStyle.Println("success")

return nil
}
1 change: 1 addition & 0 deletions cmd/pterm.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ func init() {
var (
ptermInfoMessageStyle = pterm.NewStyle(pterm.ThemeDefault.InfoMessageStyle...)
ptermSuccessMessageStyle = pterm.NewStyle(pterm.ThemeDefault.SuccessMessageStyle...)
ptermWarningMessageStyle = pterm.NewStyle(pterm.ThemeDefault.WarningMessageStyle...)
ptermErrorMessageStyle = pterm.NewStyle(pterm.ThemeDefault.ErrorMessageStyle...)
ptermScopeStyle = pterm.NewStyle(pterm.ThemeDefault.ScopeStyle...)
)
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
github.com/cenkalti/backoff/v4 v4.2.1
github.com/coreos/go-semver v0.3.1
github.com/fatih/structs v1.1.0
github.com/go-git/go-billy/v5 v5.5.0
github.com/go-git/go-git/v5 v5.11.0
github.com/hashicorp/go-retryablehttp v0.7.5
github.com/imdario/mergo v0.3.16
Expand All @@ -33,7 +34,6 @@ require (
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.5.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/gookit/color v1.5.4 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
Expand Down

0 comments on commit a2e7663

Please sign in to comment.