all repos

mugit @ 99af93d1e2d136323eef5cfec8b316d93735b94e

馃惍 git server that your cow will love
3 files changed, 73 insertions(+), 38 deletions(-)
git: fix lastCommitForFile
Author: Oleksandr Smirnov olexsmir@gmail.com
Committed at: 2026-03-05 20:37:08 +0200
Authored at: 2026-03-05 14:21:14 +0200
Change ID: mvolpyvmqqslksxwntnxnwrnqxyxqpmn
Parent: 1c4878a
M internal/git/paths.go
路路路
        18
        18
         	}

      
        19
        19
         	return path, err

      
        20
        20
         }

      
        
        21
        +

      
        
        22
        +// topLevelEntry returns the top-level entry name under base for a given path.

      
        
        23
        +// e.g. base="lua", path="lua/plugins/foo.lua" -> "plugins"

      
        
        24
        +// e.g. base="",    path="README.md"           -> "README.md"

      
        
        25
        +// returns "" if path is not under base.

      
        
        26
        +func topLevelEntry(fullPath, base string) string {

      
        
        27
        +	if base != "" && base != "." {

      
        
        28
        +		if !strings.HasPrefix(fullPath, base+"/") {

      
        
        29
        +			return ""

      
        
        30
        +		}

      
        
        31
        +		fullPath = fullPath[len(base)+1:]

      
        
        32
        +	}

      
        
        33
        +	if before, _, ok := strings.Cut(fullPath, "/"); ok {

      
        
        34
        +		return before

      
        
        35
        +	}

      
        
        36
        +	return fullPath

      
        
        37
        +}

      
M internal/git/repo.go
路路路
        192
        192
         	return newCommit(c), nil

      
        193
        193
         }

      
        194
        194
         

      
        195
        
        -func (g *Repo) lastCommitForFile(filepath string) (*Commit, error) {

      
        196
        
        -	iter, err := g.r.Log(&git.LogOptions{

      
        197
        
        -		From:  g.h,

      
        198
        
        -		Order: git.LogOrderCommitterTime,

      
        199
        
        -	})

      
        
        195
        +// lastCommitForFilesInTree ...

      
        
        196
        +// TODO: at the moment it doesn't work well with merges, ideally i would "shell" out it,

      
        
        197
        +// `git log --pretty:format:%H,%ad,%s --date=iso --name-only -- g.path`

      
        
        198
        +func (g *Repo) lastCommitForFilesInTree(tree *object.Tree, dirPath string) (map[string]*Commit, error) {

      
        
        199
        +	log, err := g.r.Log(&git.LogOptions{From: g.h})

      
        200
        200
         	if err != nil {

      
        201
        
        -		return nil, fmt.Errorf("failed to log: %w", err)

      
        
        201
        +		return nil, err

      
        202
        202
         	}

      
        203
        
        -	defer iter.Close()

      
        204
        203
         

      
        205
        
        -	var prevHash plumbing.Hash

      
        206
        
        -	var result *object.Commit

      
        207
        
        -	err = iter.ForEach(func(com *object.Commit) error {

      
        208
        
        -		tree, terr := com.Tree()

      
        209
        
        -		if terr != nil {

      
        210
        
        -			return terr

      
        
        204
        +	result := make(map[string]*Commit)

      
        
        205
        +	err = log.ForEach(func(c *object.Commit) error {

      
        
        206
        +		if c.NumParents() == 0 {

      
        
        207
        +			for _, entry := range tree.Entries {

      
        
        208
        +				if _, seen := result[entry.Name]; !seen {

      
        
        209
        +					result[entry.Name] = newCommit(c)

      
        
        210
        +				}

      
        
        211
        +			}

      
        
        212
        +			return storer.ErrStop

      
        211
        213
         		}

      
        212
        214
         

      
        213
        
        -		var hash plumbing.Hash

      
        214
        
        -		entry, eerr := tree.FindEntry(filepath)

      
        215
        
        -		if eerr == nil {

      
        216
        
        -			hash = entry.Hash

      
        217
        
        -		} else {

      
        218
        
        -			file, ferr := tree.File(filepath)

      
        219
        
        -			if ferr != nil {

      
        220
        
        -				return storer.ErrStop

      
        221
        
        -			}

      
        222
        
        -			hash = file.Hash

      
        
        215
        +		// skip merge commits

      
        
        216
        +		if c.NumParents() > 1 {

      
        
        217
        +			return nil

      
        223
        218
         		}

      
        224
        219
         

      
        225
        
        -		if hash != prevHash {

      
        226
        
        -			result = com

      
        227
        
        -			prevHash = hash

      
        
        220
        +		parent, perr := c.Parent(0)

      
        
        221
        +		if perr != nil {

      
        
        222
        +			return perr

      
        
        223
        +		}

      
        
        224
        +

      
        
        225
        +		patch, perr := parent.Patch(c)

      
        
        226
        +		if perr != nil {

      
        
        227
        +			return perr

      
        
        228
        +		}

      
        
        229
        +

      
        
        230
        +		for _, fp := range patch.FilePatches() {

      
        
        231
        +			from, to := fp.Files()

      
        
        232
        +

      
        
        233
        +			var affectedPath string

      
        
        234
        +			if to != nil {

      
        
        235
        +				affectedPath = to.Path()

      
        
        236
        +			} else if from != nil {

      
        
        237
        +				affectedPath = from.Path()

      
        
        238
        +			}

      
        
        239
        +

      
        
        240
        +			name := topLevelEntry(affectedPath, dirPath)

      
        
        241
        +			if name == "" {

      
        
        242
        +				continue

      
        
        243
        +			}

      
        
        244
        +

      
        
        245
        +			if _, seen := result[name]; !seen {

      
        
        246
        +				result[name] = newCommit(c)

      
        
        247
        +			}

      
        228
        248
         		}

      
        229
        249
         		return nil

      
        230
        250
         	})

      
        231
        
        -

      
        232
        
        -	if !errors.Is(err, storer.ErrStop) && err != nil {

      
        233
        
        -		return nil, fmt.Errorf("failed to walk commits: %w", err)

      
        234
        
        -	}

      
        235
        
        -	if result == nil {

      
        236
        
        -		return nil, fmt.Errorf("no commits found for path: %s", filepath)

      
        
        251
        +	if err != nil && !errors.Is(err, storer.ErrStop) {

      
        
        252
        +		return nil, err

      
        237
        253
         	}

      
        238
        
        -	return newCommit(result), nil

      
        
        254
        +	return result, nil

      
        239
        255
         }

      
        240
        256
         

      
        241
        257
         type Branch struct {

      
M internal/git/tree.go
路路路
        5
        5
         	"fmt"

      
        6
        6
         	"io"

      
        7
        7
         	"mime"

      
        8
        
        -	"path"

      
        9
        8
         	"path/filepath"

      
        10
        9
         	"strings"

      
        11
        10
         

      路路路
        22
        21
         

      
        23
        22
         func (g *Repo) makeNiceTree(t *object.Tree, parent string) []NiceTree {

      
        24
        23
         	var nts []NiceTree

      
        
        24
        +

      
        
        25
        +	cms, err := g.lastCommitForFilesInTree(t, parent)

      
        
        26
        +	if err != nil {

      
        
        27
        +		return nts

      
        
        28
        +	}

      
        
        29
        +

      
        25
        30
         	for _, e := range t.Entries {

      
        26
        31
         		mode, _ := e.Mode.ToOSFileMode()

      
        27
        32
         		sz, _ := t.Size(e.Name)

      
        28
        
        -

      
        29
        
        -		// TODO: this should be cached, its pretty expensive

      
        30
        
        -		lc, _ := g.lastCommitForFile(path.Join(parent, e.Name))

      
        31
        33
         		nts = append(nts, NiceTree{

      
        32
        34
         			Name:   e.Name,

      
        33
        35
         			Mode:   mode.String(),

      
        34
        36
         			IsFile: e.Mode.IsFile(),

      
        35
        
        -			Commit: lc,

      
        
        37
        +			Commit: cms[e.Name],

      
        36
        38
         			Size:   sz,

      
        37
        39
         		})

      
        38
        40
         	}