all repos

mugit @ 1a0952a361544c5465e332cac129b604cbed1922

馃惍 git server that your cow will love
8 files changed, 227 insertions(+), 180 deletions(-)
git: refactor the wrappings
Author: Oleksandr Smirnov olexsmir@gmail.com
Committed at: 2026-02-03 00:54:39 +0200
Authored at: 2026-02-02 22:29:11 +0200
Change ID: prnlunwxsrmrsosrswuwvvzlwvspmzku
Parent: 7a14044
A internal/git/config.go
路路路
        
        1
        +package git

      
        
        2
        +

      
        
        3
        +import (

      
        
        4
        +	"fmt"

      
        
        5
        +	"os"

      
        
        6
        +	"path/filepath"

      
        
        7
        +	"strings"

      
        
        8
        +	"time"

      
        
        9
        +

      
        
        10
        +	gitconfig "github.com/go-git/go-git/v5/config"

      
        
        11
        +)

      
        
        12
        +

      
        
        13
        +func (g *Repo) IsPrivate() (bool, error) {

      
        
        14
        +	v, err := g.readOption("private")

      
        
        15
        +	if err != nil {

      
        
        16
        +		return false, err

      
        
        17
        +	}

      
        
        18
        +	return v == "true", nil

      
        
        19
        +}

      
        
        20
        +

      
        
        21
        +const originRemote = "origin"

      
        
        22
        +

      
        
        23
        +func (g *Repo) IsMirror() (bool, error) {

      
        
        24
        +	r, err := g.r.Remote(originRemote)

      
        
        25
        +	if err != nil {

      
        
        26
        +		return false, fmt.Errorf("failed to get remote: %w", err)

      
        
        27
        +	}

      
        
        28
        +	return r.Config().Mirror, nil

      
        
        29
        +}

      
        
        30
        +

      
        
        31
        +func (g *Repo) SetMirrorRemote(url string) error {

      
        
        32
        +	_, err := g.r.CreateRemote(&gitconfig.RemoteConfig{

      
        
        33
        +		Name:   originRemote,

      
        
        34
        +		URLs:   []string{url},

      
        
        35
        +		Mirror: true,

      
        
        36
        +		Fetch: []gitconfig.RefSpec{

      
        
        37
        +			"+refs/*:refs/*",

      
        
        38
        +		},

      
        
        39
        +	})

      
        
        40
        +	if err != nil {

      
        
        41
        +		return fmt.Errorf("failed to create origin remote: %w", err)

      
        
        42
        +	}

      
        
        43
        +	return nil

      
        
        44
        +}

      
        
        45
        +

      
        
        46
        +func (g *Repo) RemoteURL() (string, error) {

      
        
        47
        +	r, err := g.r.Remote(originRemote)

      
        
        48
        +	if err != nil {

      
        
        49
        +		return "", fmt.Errorf("failed to get remote: %w", err)

      
        
        50
        +	}

      
        
        51
        +	return r.Config().URLs[0], nil

      
        
        52
        +}

      
        
        53
        +

      
        
        54
        +const defaultDescription = "Unnamed repository; edit this file 'description' to name the repository"

      
        
        55
        +

      
        
        56
        +func (g *Repo) Description() (string, error) {

      
        
        57
        +	path := filepath.Join(g.path, "description")

      
        
        58
        +	if _, err := os.Stat(path); err != nil {

      
        
        59
        +		return "", nil

      
        
        60
        +	}

      
        
        61
        +

      
        
        62
        +	d, err := os.ReadFile(path)

      
        
        63
        +	if err != nil {

      
        
        64
        +		return "", fmt.Errorf("failed to read description file: %w", err)

      
        
        65
        +	}

      
        
        66
        +

      
        
        67
        +	desc := string(d)

      
        
        68
        +	if strings.Contains(desc, defaultDescription) {

      
        
        69
        +		return "", nil

      
        
        70
        +	}

      
        
        71
        +	return desc, nil

      
        
        72
        +}

      
        
        73
        +

      
        
        74
        +func (g *Repo) SetDescription(desc string) error {

      
        
        75
        +	path := filepath.Join(g.path, "description")

      
        
        76
        +	return os.WriteFile(path, []byte(desc), 0o644)

      
        
        77
        +}

      
        
        78
        +

      
        
        79
        +func (g *Repo) LastSync() (time.Time, error) {

      
        
        80
        +	raw, err := g.readOption("last-sync")

      
        
        81
        +	if err != nil {

      
        
        82
        +		return time.Time{}, err

      
        
        83
        +	}

      
        
        84
        +

      
        
        85
        +	if raw == "" {

      
        
        86
        +		return time.Time{}, fmt.Errorf("last-sync not set")

      
        
        87
        +	}

      
        
        88
        +

      
        
        89
        +	out, err := time.Parse(time.RFC3339, raw)

      
        
        90
        +	if err != nil {

      
        
        91
        +		return time.Time{}, fmt.Errorf("failed to parse time: %w", err)

      
        
        92
        +	}

      
        
        93
        +

      
        
        94
        +	return out, nil

      
        
        95
        +}

      
        
        96
        +

      
        
        97
        +func (g *Repo) SetLastSync(lastSync time.Time) error {

      
        
        98
        +	return g.setOption("last-sync", lastSync.Format(time.RFC3339))

      
        
        99
        +}

      
        
        100
        +

      
        
        101
        +func (g *Repo) readOption(key string) (string, error) {

      
        
        102
        +	c, err := g.r.Config()

      
        
        103
        +	if err != nil {

      
        
        104
        +		return "", fmt.Errorf("failed to read config: %w", err)

      
        
        105
        +	}

      
        
        106
        +	return c.Raw.Section("mugit").Options.Get(key), nil

      
        
        107
        +}

      
        
        108
        +

      
        
        109
        +func (g *Repo) setOption(key, value string) error {

      
        
        110
        +	c, err := g.r.Config()

      
        
        111
        +	if err != nil {

      
        
        112
        +		return fmt.Errorf("failed to read config: %w", err)

      
        
        113
        +	}

      
        
        114
        +	c.Raw.Section("mugit").SetOption(key, value)

      
        
        115
        +	return g.r.SetConfig(c)

      
        
        116
        +}

      
M internal/git/repo.go
路路路
        3
        3
         import (

      
        4
        4
         	"errors"

      
        5
        5
         	"fmt"

      
        6
        
        -	"os"

      
        7
        6
         	"path/filepath"

      
        8
        
        -	"sort"

      
        9
        7
         	"strings"

      
        10
        8
         	"time"

      
        11
        9
         

      
        12
        10
         	"github.com/go-git/go-git/v5"

      
        13
        
        -	gitconfig "github.com/go-git/go-git/v5/config"

      
        14
        11
         	"github.com/go-git/go-git/v5/plumbing"

      
        15
        12
         	"github.com/go-git/go-git/v5/plumbing/object"

      
        16
        13
         	"github.com/go-git/go-git/v5/plumbing/transport/http"

      路路路
        59
        56
         	return g.h == plumbing.ZeroHash

      
        60
        57
         }

      
        61
        58
         

      
        62
        
        -// Init creates a bare repo.

      
        
        59
        +// Init initializes a bare repo in path.

      
        63
        60
         func Init(path string) error {

      
        64
        61
         	_, err := git.PlainInit(path, true)

      
        65
        62
         	return err

      路路路
        70
        67
         	return strings.TrimSuffix(name, ".git")

      
        71
        68
         }

      
        72
        69
         

      
        73
        
        -func (g *Repo) Commits() ([]*object.Commit, error) {

      
        
        70
        +type Commit struct {

      
        
        71
        +	Message     string

      
        
        72
        +	Hash        string

      
        
        73
        +	HashShort   string

      
        
        74
        +	AuthorName  string

      
        
        75
        +	AuthorEmail string

      
        
        76
        +	Committed   time.Time

      
        
        77
        +}

      
        
        78
        +

      
        
        79
        +func (g *Repo) Commits() ([]*Commit, error) {

      
        74
        80
         	if g.IsEmpty() {

      
        75
        
        -		return []*object.Commit{}, nil

      
        
        81
        +		return []*Commit{}, nil

      
        76
        82
         	}

      
        77
        83
         

      
        78
        84
         	ci, err := g.r.Log(&git.LogOptions{

      路路路
        83
        89
         		return nil, fmt.Errorf("commits from ref: %w", err)

      
        84
        90
         	}

      
        85
        91
         

      
        86
        
        -	commits := []*object.Commit{}

      
        
        92
        +	var commits []*Commit

      
        87
        93
         	ci.ForEach(func(c *object.Commit) error {

      
        88
        
        -		commits = append(commits, c)

      
        
        94
        +		commits = append(commits, &Commit{

      
        
        95
        +			AuthorEmail: c.Author.Email,

      
        
        96
        +			AuthorName:  c.Author.Name,

      
        
        97
        +			Committed:   c.Author.When,

      
        
        98
        +			Hash:        c.Hash.String(),

      
        
        99
        +			HashShort:   c.Hash.String()[:8],

      
        
        100
        +			Message:     c.Message,

      
        
        101
        +		})

      
        89
        102
         		return nil

      
        90
        103
         	})

      
        91
        104
         

      
        92
        105
         	return commits, nil

      
        93
        106
         }

      
        94
        107
         

      
        95
        
        -func (g *Repo) LastCommit() (*object.Commit, error) {

      
        
        108
        +func (g *Repo) LastCommit() (*Commit, error) {

      
        96
        109
         	if g.IsEmpty() {

      
        97
        110
         		return nil, ErrEmptyRepo

      
        98
        111
         	}

      路路路
        101
        114
         	if err != nil {

      
        102
        115
         		return nil, fmt.Errorf("last commit: %w", err)

      
        103
        116
         	}

      
        104
        
        -	return c, nil

      
        
        117
        +

      
        
        118
        +	return &Commit{

      
        
        119
        +		AuthorEmail: c.Author.Email,

      
        
        120
        +		AuthorName:  c.Author.Name,

      
        
        121
        +		Committed:   c.Author.When,

      
        
        122
        +		Hash:        c.Hash.String(),

      
        
        123
        +		HashShort:   c.Hash.String()[:8],

      
        
        124
        +		Message:     c.Message,

      
        
        125
        +	}, nil

      
        105
        126
         }

      
        106
        127
         

      
        107
        128
         func (g *Repo) FileContent(path string) (string, error) {

      路路路
        128
        149
         	}

      
        129
        150
         }

      
        130
        151
         

      
        131
        
        -func (g *Repo) Tags() ([]*TagReference, error) {

      
        132
        
        -	iter, err := g.r.Tags()

      
        133
        
        -	if err != nil {

      
        134
        
        -		return nil, fmt.Errorf("tag objects: %w", err)

      
        135
        
        -	}

      
        
        152
        +type Branch struct{ Name string }

      
        136
        153
         

      
        137
        
        -	tags := make([]*TagReference, 0)

      
        138
        
        -	if err := iter.ForEach(func(ref *plumbing.Reference) error {

      
        139
        
        -		obj, err := g.r.TagObject(ref.Hash())

      
        140
        
        -		switch err {

      
        141
        
        -		case nil:

      
        142
        
        -			tags = append(tags, &TagReference{

      
        143
        
        -				ref: ref,

      
        144
        
        -				tag: obj,

      
        145
        
        -			})

      
        146
        
        -		case plumbing.ErrObjectNotFound:

      
        147
        
        -			tags = append(tags, &TagReference{

      
        148
        
        -				ref: ref,

      
        149
        
        -			})

      
        150
        
        -		default:

      
        151
        
        -			return err

      
        152
        
        -		}

      
        153
        
        -		return nil

      
        154
        
        -	}); err != nil {

      
        155
        
        -		return nil, err

      
        156
        
        -	}

      
        157
        
        -

      
        158
        
        -	tagList := &TagList{r: g.r, refs: tags}

      
        159
        
        -	sort.Sort(tagList)

      
        160
        
        -	return tags, nil

      
        161
        
        -}

      
        162
        
        -

      
        163
        
        -func (g *Repo) Branches() ([]*plumbing.Reference, error) {

      
        
        154
        +func (g *Repo) Branches() ([]*Branch, error) {

      
        164
        155
         	bi, err := g.r.Branches()

      
        165
        156
         	if err != nil {

      
        166
        157
         		return nil, fmt.Errorf("branch: %w", err)

      
        167
        158
         	}

      
        168
        159
         

      
        169
        
        -	branches := []*plumbing.Reference{}

      
        170
        
        -	err = bi.ForEach(func(ref *plumbing.Reference) error {

      
        171
        
        -		branches = append(branches, ref)

      
        
        160
        +	var branches []*Branch

      
        
        161
        +	err = bi.ForEach(func(r *plumbing.Reference) error {

      
        
        162
        +		branches = append(branches, &Branch{

      
        
        163
        +			Name: r.Name().Short(),

      
        
        164
        +		})

      
        172
        165
         		return nil

      
        173
        166
         	})

      
        174
        167
         	return branches, err

      
        175
        168
         }

      
        176
        169
         

      
        177
        
        -const defaultDescription = "Unnamed repository; edit this file 'description' to name the repository"

      
        178
        
        -

      
        179
        
        -func (g *Repo) Description() (string, error) {

      
        180
        
        -	// TODO: ??? Support both mugit.description and /description file

      
        181
        
        -	path := filepath.Join(g.path, "description")

      
        182
        
        -	if _, err := os.Stat(path); err != nil {

      
        183
        
        -		return "", nil

      
        184
        
        -	}

      
        185
        
        -

      
        186
        
        -	d, err := os.ReadFile(path)

      
        187
        
        -	if err != nil {

      
        188
        
        -		return "", fmt.Errorf("failed to read description file: %w", err)

      
        189
        
        -	}

      
        190
        
        -

      
        191
        
        -	desc := string(d)

      
        192
        
        -	if strings.Contains(desc, defaultDescription) {

      
        193
        
        -		return "", nil

      
        194
        
        -	}

      
        195
        
        -

      
        196
        
        -	return desc, nil

      
        197
        
        -}

      
        198
        
        -

      
        199
        
        -func (g *Repo) IsPrivate() (bool, error) {

      
        200
        
        -	c, err := g.r.Config()

      
        201
        
        -	if err != nil {

      
        202
        
        -		return false, fmt.Errorf("failed to read config: %w", err)

      
        203
        
        -	}

      
        204
        
        -

      
        205
        
        -	s := c.Raw.Section("mugit")

      
        206
        
        -	return s.Options.Get("private") == "true", nil

      
        207
        
        -}

      
        208
        
        -

      
        209
        170
         func (g *Repo) IsGoMod() bool {

      
        210
        171
         	_, err := g.FileContent("go.mod")

      
        211
        172
         	return err == nil

      路路路
        224
        185
         	return "", fmt.Errorf("unable to find master branch")

      
        225
        186
         }

      
        226
        187
         

      
        227
        
        -type MirrorInfo struct {

      
        228
        
        -	IsMirror  bool

      
        229
        
        -	Remote    string

      
        230
        
        -	RemoteURL string

      
        231
        
        -}

      
        232
        
        -

      
        233
        
        -func (g *Repo) MirrorInfo() (MirrorInfo, error) {

      
        234
        
        -	c, err := g.r.Config()

      
        235
        
        -	if err != nil {

      
        236
        
        -		return MirrorInfo{}, fmt.Errorf("failed to read config: %w", err)

      
        237
        
        -	}

      
        238
        
        -

      
        239
        
        -	isMirror := c.Raw.Section("mugit").Options.Get("mirror") == "true"

      
        240
        
        -	for _, remote := range c.Remotes {

      
        241
        
        -		if len(remote.URLs) > 0 && (remote.Name == "upstream" || remote.Name == "origin") {

      
        242
        
        -			return MirrorInfo{

      
        243
        
        -				IsMirror:  isMirror,

      
        244
        
        -				Remote:    remote.Name,

      
        245
        
        -				RemoteURL: remote.URLs[0],

      
        246
        
        -			}, nil

      
        247
        
        -		}

      
        248
        
        -	}

      
        249
        
        -	// TODO: error if mirror opt is set, but there's no remotes

      
        250
        
        -	return MirrorInfo{}, fmt.Errorf("no mirror remote found")

      
        251
        
        -}

      
        252
        
        -

      
        253
        
        -func (g *Repo) ReadLastSync() (time.Time, error) {

      
        254
        
        -	c, err := g.r.Config()

      
        255
        
        -	if err != nil {

      
        256
        
        -		return time.Time{}, fmt.Errorf("failed to read config: %w", err)

      
        257
        
        -	}

      
        258
        
        -

      
        259
        
        -	raw := c.Raw.Section("mugit").Options.Get("last-sync")

      
        260
        
        -	if raw == "" {

      
        261
        
        -		return time.Time{}, fmt.Errorf("last-sync not set")

      
        262
        
        -	}

      
        263
        
        -

      
        264
        
        -	out, err := time.Parse(time.RFC3339, string(raw))

      
        265
        
        -	if err != nil {

      
        266
        
        -		return time.Time{}, fmt.Errorf("failed to parse time: %w", err)

      
        267
        
        -	}

      
        268
        
        -	return out, nil

      
        269
        
        -}

      
        270
        
        -

      
        271
        
        -func (g *Repo) SetLastSync(lastSync time.Time) error {

      
        272
        
        -	c, err := g.r.Config()

      
        273
        
        -	if err != nil {

      
        274
        
        -		return fmt.Errorf("failed to read config: %w", err)

      
        275
        
        -	}

      
        276
        
        -

      
        277
        
        -	c.Raw.Section("mugit").

      
        278
        
        -		SetOption("last-sync", lastSync.Format(time.RFC3339))

      
        279
        
        -	return g.r.SetConfig(c)

      
        280
        
        -}

      
        
        188
        +func (g *Repo) Fetch() error { return g.fetch(nil) }

      
        281
        189
         

      
        282
        
        -func (g *Repo) Fetch(remote string) error {

      
        283
        
        -	return g.fetch(remote, nil)

      
        284
        
        -}

      
        285
        
        -

      
        286
        
        -func (g *Repo) FetchFromGithubWithToken(remote, token string) error {

      
        287
        
        -	return g.fetch(remote, &http.BasicAuth{

      
        
        190
        +func (g *Repo) FetchFromGithubWithToken(token string) error {

      
        
        191
        +	return g.fetch(&http.BasicAuth{

      
        288
        192
         		Username: token,

      
        289
        193
         		Password: "x-oauth-basic",

      
        290
        194
         	})

      
        291
        195
         }

      
        292
        196
         

      
        293
        
        -func (g *Repo) fetch(remote string, auth http.AuthMethod) error {

      
        294
        
        -	rmt, err := g.r.Remote(remote)

      
        
        197
        +func (g *Repo) fetch(auth http.AuthMethod) error {

      
        
        198
        +	rmt, err := g.r.Remote(originRemote)

      
        295
        199
         	if err != nil {

      
        296
        
        -		return fmt.Errorf("failed to get upstream remote: %w", err)

      
        
        200
        +		return fmt.Errorf("failed to get remote: %w", err)

      
        297
        201
         	}

      
        298
        202
         

      
        299
        
        -	if ferr := rmt.Fetch(

      
        300
        
        -		&git.FetchOptions{

      
        301
        
        -			RefSpecs: []gitconfig.RefSpec{

      
        302
        
        -				// fetch all branches

      
        303
        
        -				"+refs/heads/*:refs/heads/*",

      
        304
        
        -				"+refs/tags/*:refs/tags/*",

      
        305
        
        -			},

      
        306
        
        -			Auth:  auth,

      
        307
        
        -			Tags:  git.AllTags,

      
        308
        
        -			Prune: true,

      
        309
        
        -			Force: true,

      
        310
        
        -		}); ferr != nil && !errors.Is(ferr, git.NoErrAlreadyUpToDate) {

      
        311
        
        -		return fmt.Errorf("fetch failed: %w", ferr)

      
        312
        
        -	}

      
        313
        
        -	return err

      
        
        203
        +	return rmt.Fetch(&git.FetchOptions{

      
        
        204
        +		Auth:  auth,

      
        
        205
        +		Tags:  git.AllTags,

      
        
        206
        +		Prune: true,

      
        
        207
        +		Force: true,

      
        
        208
        +	})

      
        314
        209
         }

      
M internal/git/tags.go
路路路
        1
        1
         package git

      
        2
        2
         

      
        3
        3
         import (

      
        
        4
        +	"fmt"

      
        
        5
        +	"sort"

      
        4
        6
         	"time"

      
        5
        7
         

      
        6
        8
         	"github.com/go-git/go-git/v5"

      路路路
        30
        32
         		return t.tag.Message

      
        31
        33
         	}

      
        32
        34
         	return ""

      
        
        35
        +}

      
        
        36
        +

      
        
        37
        +func (g *Repo) Tags() ([]*TagReference, error) {

      
        
        38
        +	iter, err := g.r.Tags()

      
        
        39
        +	if err != nil {

      
        
        40
        +		return nil, fmt.Errorf("tag objects: %w", err)

      
        
        41
        +	}

      
        
        42
        +

      
        
        43
        +	tags := make([]*TagReference, 0)

      
        
        44
        +	if err := iter.ForEach(func(ref *plumbing.Reference) error {

      
        
        45
        +		obj, err := g.r.TagObject(ref.Hash())

      
        
        46
        +		switch err {

      
        
        47
        +		case nil:

      
        
        48
        +			tags = append(tags, &TagReference{

      
        
        49
        +				ref: ref,

      
        
        50
        +				tag: obj,

      
        
        51
        +			})

      
        
        52
        +		case plumbing.ErrObjectNotFound:

      
        
        53
        +			tags = append(tags, &TagReference{

      
        
        54
        +				ref: ref,

      
        
        55
        +			})

      
        
        56
        +		default:

      
        
        57
        +			return err

      
        
        58
        +		}

      
        
        59
        +		return nil

      
        
        60
        +	}); err != nil {

      
        
        61
        +		return nil, err

      
        
        62
        +	}

      
        
        63
        +

      
        
        64
        +	tagList := &TagList{r: g.r, refs: tags}

      
        
        65
        +	sort.Sort(tagList)

      
        
        66
        +

      
        
        67
        +	return tags, nil

      
        33
        68
         }

      
        34
        69
         

      
        35
        70
         func (t *TagList) Len() int {

      
M internal/handlers/repo.go
路路路
        109
        109
         	data["commits"] = commits

      
        110
        110
         	data["gomod"] = repo.IsGoMod()

      
        111
        111
         

      
        112
        
        -	if mirrorInfo, err := repo.MirrorInfo(); err == nil && mirrorInfo.IsMirror {

      
        113
        
        -		lastSync, _ := repo.ReadLastSync()

      
        
        112
        +	if isMirror, err := repo.IsMirror(); err == nil && isMirror {

      
        
        113
        +		lastSync, _ := repo.LastSync()

      
        
        114
        +		remoteURL, _ := repo.RemoteURL()

      
        114
        115
         		data["mirrorinfo"] = map[string]any{

      
        115
        116
         			"isMirror": true,

      
        116
        
        -			"url":      mirrorInfo.RemoteURL,

      
        
        117
        +			"url":      remoteURL,

      
        117
        118
         			"lastSync": lastSync,

      
        118
        119
         		}

      
        119
        120
         	}

      路路路
        411
        412
         				continue

      
        412
        413
         			}

      
        413
        414
         		} else {

      
        414
        
        -			lastCommitTime = lastCommit.Author.When

      
        
        415
        +			lastCommitTime = lastCommit.Committed

      
        415
        416
         		}

      
        416
        417
         

      
        417
        418
         		repos = append(repos, repoList{

      
M internal/mirror/mirror.go
路路路
        89
        89
         	name := repo.Name()

      
        90
        90
         	slog.Info("mirror: sync started", "repo", name)

      
        91
        91
         

      
        92
        
        -	mi, err := repo.MirrorInfo()

      
        
        92
        +	remoteURL, err := repo.RemoteURL()

      
        93
        93
         	if err != nil {

      
        94
        
        -		slog.Error("mirror: failed to get info", "repo", name, "err", err)

      
        
        94
        +		slog.Error("mirror: failed to get remote url", "repo", name, "err", err)

      
        95
        95
         		return err

      
        96
        96
         	}

      
        97
        97
         

      
        98
        
        -	if err := w.isRemoteValid(mi.RemoteURL); err != nil {

      
        
        98
        +	if err := w.isRemoteValid(remoteURL); err != nil {

      
        99
        99
         		slog.Error("mirror: remote is not valid", "repo", name, "err", err)

      
        100
        100
         		return err

      
        101
        101
         	}

      
        102
        102
         

      
        103
        
        -	if w.isRemoteGithub(mi.RemoteURL) && w.c.Mirror.GithubToken != "" {

      
        104
        
        -		if err := repo.FetchFromGithubWithToken(mi.Remote, w.c.Mirror.GithubToken); err != nil {

      
        
        103
        +	if w.isRemoteGithub(remoteURL) && w.c.Mirror.GithubToken != "" {

      
        
        104
        +		if err := repo.FetchFromGithubWithToken(w.c.Mirror.GithubToken); err != nil {

      
        105
        105
         			slog.Error("mirror: fetch failed (authorized)", "repo", name, "err", err)

      
        106
        106
         			return err

      
        107
        107
         		}

      
        108
        108
         	} else {

      
        109
        
        -		if err := repo.Fetch(mi.Remote); err != nil {

      
        
        109
        +		if err := repo.Fetch(); err != nil {

      
        110
        110
         			slog.Error("mirror: fetch failed", "repo", name, "err", err)

      
        111
        111
         			return err

      
        112
        112
         		}

      路路路
        139
        139
         			continue

      
        140
        140
         		}

      
        141
        141
         

      
        142
        
        -		mirror, err := repo.MirrorInfo()

      
        
        142
        +		isMirror, err := repo.IsMirror()

      
        143
        143
         		if err != nil {

      
        144
        144
         			slog.Debug("skipping non-mirror repo", "path", name, "err", err)

      
        145
        145
         			continue

      
        146
        146
         		}

      
        147
        147
         

      
        148
        
        -		if mirror.IsMirror {

      
        
        148
        +		if isMirror {

      
        149
        149
         			repos = append(repos, repo)

      
        150
        150
         		}

      
        151
        151
         	}

      
M web/templates/repo_index.html
路路路
        18
        18
                   {{ range .commits }}

      
        19
        19
                   <div class="box">

      
        20
        20
                     <div>

      
        21
        
        -              <a href="/{{ $repo }}/commit/{{ .Hash.String }}" class="commit-hash">{{ slice .Hash.String 0 8 }}</a>

      
        22
        
        -              &mdash; {{ .Author.Name }}

      
        23
        
        -              <span class="commit-date commit-info">{{ .Author.When.Format "Mon, 02 Jan 2006" }}</span>

      
        
        21
        +              <a href="/{{ $repo }}/commit/{{ .Hash }}" class="commit-hash">{{ .HashShort }}</a>

      
        
        22
        +              &mdash; {{ .AuthorName }}

      
        
        23
        +              <span class="commit-date commit-info">{{ .Committed.Format "Mon, 02 Jan 2006" }}</span>

      
        24
        24
                     </div>

      
        25
        25
                     <div>{{ commitSummary .Message }}</div>

      
        26
        26
                   </div>

      
M web/templates/repo_log.html
路路路
        22
        22
                   {{ range .commits }}

      
        23
        23
                   <tr>

      
        24
        24
                     <td class="msg">

      
        25
        
        -              <a href="/{{ $repo }}/commit/{{ .Hash.String }}">{{ commitSummary .Message }}</a>

      
        
        25
        +              <a href="/{{ $repo }}/commit/{{ .Hash }}">{{ commitSummary .Message }}</a>

      
        26
        26
                     </td>

      
        27
        27
                     <td class="author">

      
        28
        28
                       <span class="author-short">

      
        29
        
        -                {{ .Author.Name }} <a href="mailto:{{ .Author.Email }}" class="commit-email">{{ .Author.Email }}</a>

      
        
        29
        +                {{ .AuthorName }} <a href="mailto:{{ .AuthorEmail }}" class="commit-email">{{ .AuthorEmail }}</a>

      
        30
        30
                       </span>

      
        31
        31
                       <span class="tooltip" role="tooltip">

      
        32
        
        -                <strong>{{ .Author.Name }}</strong><br>

      
        33
        
        -                <a href="mailto:{{ .Author.Email }}" class="commit-email">{{ .Author.Email }}</a>

      
        
        32
        +                <strong>{{ .AuthorName }}</strong><br>

      
        
        33
        +                <a href="mailto:{{ .AuthorEmail }}" class="commit-email">{{ .AuthorEmail }}</a>

      
        34
        34
                       </span>

      
        35
        35
                     </td>

      
        36
        36
                     <td class="">

      
        37
        
        -              <a href="/{{ $repo }}/commit/{{ .Hash.String }}">{{ slice .Hash.String 0 8 }}</a>

      
        
        37
        +              <a href="/{{ $repo }}/commit/{{ .Hash }}">{{ .HashShort }}</a>

      
        38
        38
                     </td>

      
        39
        39
                     <td class="age">

      
        40
        
        -              <span class="age-display">{{ humanizeTime .Committer.When }}</span>

      
        41
        
        -              <span class="tooltip" role="tooltip">{{ .Committer.When }}</span>

      
        
        40
        +              <span class="age-display">{{ humanizeTime .Committed }}</span>

      
        
        41
        +              <span class="tooltip" role="tooltip">{{ .Committed }}</span>

      
        42
        42
                     </td>

      
        43
        43
                   </tr>

      
        44
        44
                   {{ end }}

      
M web/templates/repo_refs.html
路路路
        12
        12
               <div class="refs">

      
        13
        13
               {{ range .branches }}

      
        14
        14
                 <div>

      
        15
        
        -        <strong>{{ .Name.Short }}</strong>

      
        16
        
        -        <a href="/{{ $name }}/tree/{{ .Name.Short }}/">browse</a>

      
        17
        
        -        <a href="/{{ $name }}/log/{{ .Name.Short }}">log</a>

      
        
        15
        +        <strong>{{ .Name }}</strong>

      
        
        16
        +        <a href="/{{ $name }}/tree/{{ .Name }}/">browse</a>

      
        
        17
        +        <a href="/{{ $name }}/log/{{ .Name }}">log</a>

      
        18
        18
                 </div>

      
        19
        19
               {{ end }}

      
        20
        20
               </div>