forked from ChappIO/git-backup
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrepository.go
More file actions
131 lines (116 loc) · 3.02 KB
/
Copy pathrepository.go
File metadata and controls
131 lines (116 loc) · 3.02 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package git_backup
import (
"errors"
"log"
"net/url"
"os"
"strings"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing/transport"
"github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/go-git/go-git/v5/plumbing/transport/ssh"
)
type RepositorySource interface {
GetName() string
Test() error
ListRepositories() ([]*Repository, error)
GetSshKeyPath() string
}
type Repository struct {
GitURL url.URL
FullName string
}
func isBare(repo *git.Repository) (bool, error) {
config, err := repo.Config()
if err != nil {
return false, err
}
return config.Core.IsBare, nil
}
func (r *Repository) CloneInto(path string, bare bool, sshKeyPath string) error {
var auth transport.AuthMethod
var cloneURL string
if sshKeyPath != "" {
// Convert HTTP URL to SSH URL
sshURL := convertToSSHURL(r.GitURL)
cloneURL = sshURL
// Load SSH key
publicKeys, err := ssh.NewPublicKeysFromFile("git", sshKeyPath, "")
if err != nil {
return err
}
auth = publicKeys
} else {
// Use HTTP authentication
cloneURL = r.GitURL.String()
if r.GitURL.User != nil {
password, _ := r.GitURL.User.Password()
auth = &http.BasicAuth{
Username: r.GitURL.User.Username(),
Password: password,
}
}
}
gitRepo, err := git.PlainClone(path, bare, &git.CloneOptions{
URL: cloneURL,
Auth: auth,
Progress: os.Stdout,
Depth: 999,
Tags: git.AllTags,
})
if errors.Is(err, git.ErrRepositoryAlreadyExists) {
// Pull instead of clone
if gitRepo, err = git.PlainOpen(path); err == nil {
// we need to check whether it's a bare repo or not.
// if not we should pull, if it is then pull won't work
if isBare, bErr := isBare(gitRepo); bErr == nil && !isBare {
if w, wErr := gitRepo.Worktree(); wErr != nil {
err = wErr
} else {
err = w.Pull(&git.PullOptions{
Auth: auth,
Progress: os.Stdout,
})
}
}
}
}
switch {
case errors.Is(err, transport.ErrEmptyRemoteRepository):
log.Printf("%s is an empty repository", r.FullName)
// Empty repo does not need backup
return nil
default:
return err
case errors.Is(err, git.NoErrAlreadyUpToDate):
log.Printf("No need to pull, %s is already up-to-date", r.FullName)
// Already up to date on current branch, still need to refresh other branches
fallthrough
case err == nil:
// No errors, continue
err = gitRepo.Fetch(&git.FetchOptions{
Auth: auth,
Progress: os.Stdout,
Tags: git.AllTags,
Force: true,
})
}
switch err {
case git.NoErrAlreadyUpToDate:
log.Printf("No need to fetch, %s is already up-to-date", r.FullName)
return nil
default:
return err
}
}
// convertToSSHURL converts an HTTP/HTTPS Git URL to SSH format
// Example: https://github.com/user/repo.git -> git@github.com:user/repo.git
func convertToSSHURL(httpURL url.URL) string {
host := httpURL.Hostname()
path := strings.TrimPrefix(httpURL.Path, "/")
// Ensure path ends with .git
if !strings.HasSuffix(path, ".git") {
path = path + ".git"
}
return "git@" + host + ":" + path
}