@@ -22,42 +22,63 @@ import (
22
22
"github.com/hashicorp/go-version"
23
23
)
24
24
25
- // RequiredVersion is the minimum Git version required
26
- const RequiredVersion = "2.0.0"
25
+ const RequiredVersion = "2.0.0" // the minimum Git version required
27
26
28
- var (
29
- // GitExecutable is the command name of git
30
- // Could be updated to an absolute path while initialization
31
- GitExecutable = "git"
32
-
33
- // DefaultContext is the default context to run git commands in, must be initialized by git.InitXxx
34
- DefaultContext context.Context
27
+ type Features struct {
28
+ gitVersion * version.Version
35
29
36
- DefaultFeatures struct {
37
- GitVersion * version.Version
30
+ UsingGogit bool
31
+ SupportProcReceive bool // >= 2.29
32
+ SupportHashSha256 bool // >= 2.42, SHA-256 repositories no longer an ‘experimental curiosity’
33
+ SupportedObjectFormats []ObjectFormat // sha1, sha256
34
+ }
38
35
39
- SupportProcReceive bool // >= 2.29
40
- SupportHashSha256 bool // >= 2.42, SHA-256 repositories no longer an ‘experimental curiosity’
41
- }
36
+ var (
37
+ GitExecutable = "git" // the command name of git, will be updated to an absolute path during initialization
38
+ DefaultContext context.Context // the default context to run git commands in, must be initialized by git.InitXxx
39
+ defaultFeatures * Features
42
40
)
43
41
44
- // loadGitVersion tries to get the current git version and stores it into a global variable
45
- func loadGitVersion () error {
46
- // doesn't need RWMutex because it's executed by Init()
47
- if DefaultFeatures .GitVersion != nil {
48
- return nil
42
+ func (f * Features ) CheckVersionAtLeast (atLeast string ) bool {
43
+ return f .gitVersion .Compare (version .Must (version .NewVersion (atLeast ))) >= 0
44
+ }
45
+
46
+ // VersionInfo returns git version information
47
+ func (f * Features ) VersionInfo () string {
48
+ return f .gitVersion .Original ()
49
+ }
50
+
51
+ func DefaultFeatures () * Features {
52
+ if defaultFeatures == nil {
53
+ if ! setting .IsProd || setting .IsInTesting {
54
+ log .Warn ("git.DefaultFeatures is called before git.InitXxx, initializing with default values" )
55
+ }
56
+ if err := InitSimple (context .Background ()); err != nil {
57
+ log .Fatal ("git.InitSimple failed: %v" , err )
58
+ }
49
59
}
60
+ return defaultFeatures
61
+ }
50
62
63
+ func loadGitVersionFeatures () (* Features , error ) {
51
64
stdout , _ , runErr := NewCommand (DefaultContext , "version" ).RunStdString (nil )
52
65
if runErr != nil {
53
- return runErr
66
+ return nil , runErr
54
67
}
55
68
56
69
ver , err := parseGitVersionLine (strings .TrimSpace (stdout ))
57
- if err = = nil {
58
- DefaultFeatures . GitVersion = ver
70
+ if err ! = nil {
71
+ return nil , err
59
72
}
60
- return err
73
+
74
+ features := & Features {gitVersion : ver , UsingGogit : isGogit }
75
+ features .SupportProcReceive = features .CheckVersionAtLeast ("2.29" )
76
+ features .SupportHashSha256 = features .CheckVersionAtLeast ("2.42" ) && ! isGogit
77
+ features .SupportedObjectFormats = []ObjectFormat {Sha1ObjectFormat }
78
+ if features .SupportHashSha256 {
79
+ features .SupportedObjectFormats = append (features .SupportedObjectFormats , Sha256ObjectFormat )
80
+ }
81
+ return features , nil
61
82
}
62
83
63
84
func parseGitVersionLine (s string ) (* version.Version , error ) {
@@ -85,56 +106,24 @@ func SetExecutablePath(path string) error {
85
106
return fmt .Errorf ("git not found: %w" , err )
86
107
}
87
108
GitExecutable = absPath
109
+ return nil
110
+ }
88
111
89
- if err = loadGitVersion (); err != nil {
90
- return fmt .Errorf ("unable to load git version: %w" , err )
91
- }
92
-
93
- versionRequired , err := version .NewVersion (RequiredVersion )
94
- if err != nil {
95
- return err
96
- }
97
-
98
- if DefaultFeatures .GitVersion .LessThan (versionRequired ) {
112
+ func ensureGitVersion () error {
113
+ if ! DefaultFeatures ().CheckVersionAtLeast (RequiredVersion ) {
99
114
moreHint := "get git: https://git-scm.com/download/"
100
115
if runtime .GOOS == "linux" {
101
116
// there are a lot of CentOS/RHEL users using old git, so we add a special hint for them
102
- if _ , err = os .Stat ("/etc/redhat-release" ); err == nil {
117
+ if _ , err : = os .Stat ("/etc/redhat-release" ); err == nil {
103
118
// ius.io is the recommended official(git-scm.com) method to install git
104
119
moreHint = "get git: https://git-scm.com/download/linux and https://ius.io"
105
120
}
106
121
}
107
- return fmt .Errorf ("installed git version %q is not supported, Gitea requires git version >= %q, %s" , DefaultFeatures . GitVersion .Original (), RequiredVersion , moreHint )
122
+ return fmt .Errorf ("installed git version %q is not supported, Gitea requires git version >= %q, %s" , DefaultFeatures (). gitVersion .Original (), RequiredVersion , moreHint )
108
123
}
109
124
110
- if err = checkGitVersionCompatibility (DefaultFeatures .GitVersion ); err != nil {
111
- return fmt .Errorf ("installed git version %s has a known compatibility issue with Gitea: %w, please upgrade (or downgrade) git" , DefaultFeatures .GitVersion .String (), err )
112
- }
113
- return nil
114
- }
115
-
116
- // VersionInfo returns git version information
117
- func VersionInfo () string {
118
- if DefaultFeatures .GitVersion == nil {
119
- return "(git not found)"
120
- }
121
- format := "%s"
122
- args := []any {DefaultFeatures .GitVersion .Original ()}
123
- // Since git wire protocol has been released from git v2.18
124
- if setting .Git .EnableAutoGitWireProtocol && CheckGitVersionAtLeast ("2.18" ) == nil {
125
- format += ", Wire Protocol %s Enabled"
126
- args = append (args , "Version 2" ) // for focus color
127
- }
128
-
129
- return fmt .Sprintf (format , args ... )
130
- }
131
-
132
- func checkInit () error {
133
- if setting .Git .HomePath == "" {
134
- return errors .New ("unable to init Git's HomeDir, incorrect initialization of the setting and git modules" )
135
- }
136
- if DefaultContext != nil {
137
- log .Warn ("git module has been initialized already, duplicate init may work but it's better to fix it" )
125
+ if err := checkGitVersionCompatibility (DefaultFeatures ().gitVersion ); err != nil {
126
+ return fmt .Errorf ("installed git version %s has a known compatibility issue with Gitea: %w, please upgrade (or downgrade) git" , DefaultFeatures ().gitVersion .String (), err )
138
127
}
139
128
return nil
140
129
}
@@ -154,8 +143,12 @@ func HomeDir() string {
154
143
// InitSimple initializes git module with a very simple step, no config changes, no global command arguments.
155
144
// This method doesn't change anything to filesystem. At the moment, it is only used by some Gitea sub-commands.
156
145
func InitSimple (ctx context.Context ) error {
157
- if err := checkInit (); err != nil {
158
- return err
146
+ if setting .Git .HomePath == "" {
147
+ return errors .New ("unable to init Git's HomeDir, incorrect initialization of the setting and git modules" )
148
+ }
149
+
150
+ if DefaultContext != nil && (! setting .IsProd || setting .IsInTesting ) {
151
+ log .Warn ("git module has been initialized already, duplicate init may work but it's better to fix it" )
159
152
}
160
153
161
154
DefaultContext = ctx
@@ -165,40 +158,45 @@ func InitSimple(ctx context.Context) error {
165
158
defaultCommandExecutionTimeout = time .Duration (setting .Git .Timeout .Default ) * time .Second
166
159
}
167
160
168
- return SetExecutablePath (setting .Git .Path )
169
- }
161
+ if err := SetExecutablePath (setting .Git .Path ); err != nil {
162
+ return err
163
+ }
170
164
171
- // InitFull initializes git module with version check and change global variables, sync gitconfig.
172
- // It should only be called once at the beginning of the program initialization (TestMain/GlobalInitInstalled) as this code makes unsynchronized changes to variables.
173
- func InitFull (ctx context.Context ) (err error ) {
174
- if err = InitSimple (ctx ); err != nil {
165
+ var err error
166
+ defaultFeatures , err = loadGitVersionFeatures ()
167
+ if err != nil {
168
+ return err
169
+ }
170
+ if err = ensureGitVersion (); err != nil {
175
171
return err
176
172
}
177
173
178
174
// when git works with gnupg (commit signing), there should be a stable home for gnupg commands
179
175
if _ , ok := os .LookupEnv ("GNUPGHOME" ); ! ok {
180
176
_ = os .Setenv ("GNUPGHOME" , filepath .Join (HomeDir (), ".gnupg" ))
181
177
}
178
+ return nil
179
+ }
180
+
181
+ // InitFull initializes git module with version check and change global variables, sync gitconfig.
182
+ // It should only be called once at the beginning of the program initialization (TestMain/GlobalInitInstalled) as this code makes unsynchronized changes to variables.
183
+ func InitFull (ctx context.Context ) (err error ) {
184
+ if err = InitSimple (ctx ); err != nil {
185
+ return err
186
+ }
182
187
183
188
// Since git wire protocol has been released from git v2.18
184
- if setting .Git .EnableAutoGitWireProtocol && CheckGitVersionAtLeast ( "2.18" ) == nil {
189
+ if setting .Git .EnableAutoGitWireProtocol && DefaultFeatures (). CheckVersionAtLeast ( "2.18" ) {
185
190
globalCommandArgs = append (globalCommandArgs , "-c" , "protocol.version=2" )
186
191
}
187
192
188
193
// Explicitly disable credential helper, otherwise Git credentials might leak
189
- if CheckGitVersionAtLeast ( "2.9" ) == nil {
194
+ if DefaultFeatures (). CheckVersionAtLeast ( "2.9" ) {
190
195
globalCommandArgs = append (globalCommandArgs , "-c" , "credential.helper=" )
191
196
}
192
- DefaultFeatures .SupportProcReceive = CheckGitVersionAtLeast ("2.29" ) == nil
193
- DefaultFeatures .SupportHashSha256 = CheckGitVersionAtLeast ("2.42" ) == nil && ! isGogit
194
- if DefaultFeatures .SupportHashSha256 {
195
- SupportedObjectFormats = append (SupportedObjectFormats , Sha256ObjectFormat )
196
- } else {
197
- log .Warn ("sha256 hash support is disabled - requires Git >= 2.42. Gogit is currently unsupported" )
198
- }
199
197
200
198
if setting .LFS .StartServer {
201
- if CheckGitVersionAtLeast ( "2.1.2" ) != nil {
199
+ if ! DefaultFeatures (). CheckVersionAtLeast ( "2.1.2" ) {
202
200
return errors .New ("LFS server support requires Git >= 2.1.2" )
203
201
}
204
202
globalCommandArgs = append (globalCommandArgs , "-c" , "filter.lfs.required=" , "-c" , "filter.lfs.smudge=" , "-c" , "filter.lfs.clean=" )
@@ -238,13 +236,13 @@ func syncGitConfig() (err error) {
238
236
return err
239
237
}
240
238
241
- if CheckGitVersionAtLeast ( "2.10" ) == nil {
239
+ if DefaultFeatures (). CheckVersionAtLeast ( "2.10" ) {
242
240
if err := configSet ("receive.advertisePushOptions" , "true" ); err != nil {
243
241
return err
244
242
}
245
243
}
246
244
247
- if CheckGitVersionAtLeast ( "2.18" ) == nil {
245
+ if DefaultFeatures (). CheckVersionAtLeast ( "2.18" ) {
248
246
if err := configSet ("core.commitGraph" , "true" ); err != nil {
249
247
return err
250
248
}
@@ -256,7 +254,7 @@ func syncGitConfig() (err error) {
256
254
}
257
255
}
258
256
259
- if DefaultFeatures .SupportProcReceive {
257
+ if DefaultFeatures () .SupportProcReceive {
260
258
// set support for AGit flow
261
259
if err := configAddNonExist ("receive.procReceiveRefs" , "refs/for" ); err != nil {
262
260
return err
@@ -294,7 +292,7 @@ func syncGitConfig() (err error) {
294
292
}
295
293
296
294
// By default partial clones are disabled, enable them from git v2.22
297
- if ! setting .Git .DisablePartialClone && CheckGitVersionAtLeast ( "2.22" ) == nil {
295
+ if ! setting .Git .DisablePartialClone && DefaultFeatures (). CheckVersionAtLeast ( "2.22" ) {
298
296
if err = configSet ("uploadpack.allowfilter" , "true" ); err != nil {
299
297
return err
300
298
}
@@ -309,21 +307,6 @@ func syncGitConfig() (err error) {
309
307
return err
310
308
}
311
309
312
- // CheckGitVersionAtLeast check git version is at least the constraint version
313
- func CheckGitVersionAtLeast (atLeast string ) error {
314
- if DefaultFeatures .GitVersion == nil {
315
- panic ("git module is not initialized" ) // it shouldn't happen
316
- }
317
- atLeastVersion , err := version .NewVersion (atLeast )
318
- if err != nil {
319
- return err
320
- }
321
- if DefaultFeatures .GitVersion .Compare (atLeastVersion ) < 0 {
322
- return fmt .Errorf ("installed git binary version %s is not at least %s" , DefaultFeatures .GitVersion .Original (), atLeast )
323
- }
324
- return nil
325
- }
326
-
327
310
func checkGitVersionCompatibility (gitVer * version.Version ) error {
328
311
badVersions := []struct {
329
312
Version * version.Version
0 commit comments