aboutsummaryrefslogtreecommitdiff
path: root/git_misc.go
blob: f2f8dba40ace4df3b4f4270d372d124e7cf79666 (plain) (blame)
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// SPDX-License-Identifier: AGPL-3.0-only
// SPDX-FileContributor: Runxi Yu <https://runxiyu.org>

package main

import (
	"context"
	"errors"
	"io"
	"os"
	"strings"

	"github.com/go-git/go-git/v5"
	"github.com/go-git/go-git/v5/plumbing"
	"github.com/go-git/go-git/v5/plumbing/object"
	"github.com/jackc/pgx/v5/pgtype"
)

// open_git_repo opens a git repository by group and repo name.
func open_git_repo(ctx context.Context, group_path []string, repo_name string) (repo *git.Repository, description string, repo_id int, err error) {
	var fs_path string

	err = database.QueryRow(ctx, `
WITH RECURSIVE group_path_cte AS (
	-- Start: match the first name in the path where parent_group IS NULL
	SELECT
		id,
		parent_group,
		name,
		1 AS depth
	FROM groups
	WHERE name = ($1::text[])[1]
		AND parent_group IS NULL

	UNION ALL

	-- Recurse: join next segment of the path
	SELECT
		g.id,
		g.parent_group,
		g.name,
		group_path_cte.depth + 1
	FROM groups g
	JOIN group_path_cte ON g.parent_group = group_path_cte.id
	WHERE g.name = ($1::text[])[group_path_cte.depth + 1]
		AND group_path_cte.depth + 1 <= cardinality($1::text[])
)
SELECT
	r.filesystem_path,
	COALESCE(r.description, ''),
	r.id
FROM group_path_cte g
JOIN repos r ON r.group_id = g.id
WHERE g.depth = cardinality($1::text[])
	AND r.name = $2
	`, pgtype.FlatArray[string](group_path), repo_name).Scan(&fs_path, &description, &repo_id)
	if err != nil {
		return
	}

	repo, err = git.PlainOpen(fs_path)
	return
}

// go-git's tree entries are not friendly for use in HTML templates.
type display_git_tree_entry_t struct {
	Name       string
	Mode       string
	Size       int64
	Is_file    bool
	Is_subtree bool
}

func build_display_git_tree(tree *object.Tree) (display_git_tree []display_git_tree_entry_t) {
	for _, entry := range tree.Entries {
		display_git_tree_entry := display_git_tree_entry_t{}
		var err error
		var os_mode os.FileMode

		if os_mode, err = entry.Mode.ToOSFileMode(); err != nil {
			display_git_tree_entry.Mode = "x---------"
		} else {
			display_git_tree_entry.Mode = os_mode.String()
		}

		display_git_tree_entry.Is_file = entry.Mode.IsFile()

		if display_git_tree_entry.Size, err = tree.Size(entry.Name); err != nil {
			display_git_tree_entry.Size = 0
		}

		display_git_tree_entry.Name = strings.TrimPrefix(entry.Name, "/")

		display_git_tree = append(display_git_tree, display_git_tree_entry)
	}
	return display_git_tree
}

func get_recent_commits(repo *git.Repository, head_hash plumbing.Hash, number_of_commits int) (recent_commits []*object.Commit, err error) {
	var commit_iter object.CommitIter
	var this_recent_commit *object.Commit

	commit_iter, err = repo.Log(&git.LogOptions{From: head_hash})
	if err != nil {
		return nil, err
	}
	recent_commits = make([]*object.Commit, 0)
	defer commit_iter.Close()
	if number_of_commits < 0 {
		for {
			this_recent_commit, err = commit_iter.Next()
			if errors.Is(err, io.EOF) {
				return recent_commits, nil
			} else if err != nil {
				return nil, err
			}
			recent_commits = append(recent_commits, this_recent_commit)
		}
	} else {
		for range number_of_commits {
			this_recent_commit, err = commit_iter.Next()
			if errors.Is(err, io.EOF) {
				return recent_commits, nil
			} else if err != nil {
				return nil, err
			}
			recent_commits = append(recent_commits, this_recent_commit)
		}
	}
	return recent_commits, err
}

func get_patch_from_commit(commit_object *object.Commit) (parent_commit_hash plumbing.Hash, patch *object.Patch, err error) {
	var parent_commit_object *object.Commit
	var commit_tree *object.Tree

	parent_commit_object, err = commit_object.Parent(0)
	if errors.Is(err, object.ErrParentNotFound) {
		if commit_tree, err = commit_object.Tree(); err != nil {
			return
		}
		if patch, err = (&object.Tree{}).Patch(commit_tree); err != nil {
			return
		}
	} else if err != nil {
		return
	} else {
		parent_commit_hash = parent_commit_object.Hash
		if patch, err = parent_commit_object.Patch(commit_object); err != nil {
			return
		}
	}
	return
}