2016-11-03 16:16:01 -06:00
// Copyright 2015 The Gogs Authors. All rights reserved.
2017-04-28 08:20:58 -06:00
// Copyright 2017 The Gitea Authors. All rights reserved.
2022-11-27 11:20:29 -07:00
// SPDX-License-Identifier: MIT
2016-11-03 16:16:01 -06:00
package git
import (
2019-11-30 07:40:22 -07:00
"context"
2022-06-16 09:47:44 -06:00
"errors"
2016-11-03 16:16:01 -06:00
"fmt"
2022-05-02 06:30:24 -06:00
"os"
2019-04-17 05:11:37 -06:00
"os/exec"
2022-07-08 02:09:07 -06:00
"path/filepath"
2022-06-19 05:56:22 -06:00
"regexp"
2019-11-01 23:40:49 -06:00
"runtime"
2016-11-03 16:16:01 -06:00
"strings"
"time"
2017-04-28 08:20:58 -06:00
2022-06-09 19:57:49 -06:00
"code.gitea.io/gitea/modules/log"
2021-06-26 05:28:55 -06:00
"code.gitea.io/gitea/modules/setting"
2022-07-15 07:01:32 -06:00
2020-09-05 10:42:58 -06:00
"github.com/hashicorp/go-version"
2016-11-03 16:16:01 -06:00
)
2022-08-08 21:22:24 -06:00
// RequiredVersion is the minimum Git version required
const RequiredVersion = "2.0.0"
2019-04-17 05:11:37 -06:00
2022-06-16 09:47:44 -06:00
var (
2019-04-17 05:11:37 -06:00
// GitExecutable is the command name of git
// Could be updated to an absolute path while initialization
GitExecutable = "git"
2019-05-14 19:57:00 -06:00
2022-06-16 09:47:44 -06:00
// DefaultContext is the default context to run git commands in, must be initialized by git.InitXxx
DefaultContext context . Context
2019-11-30 07:40:22 -07:00
2024-02-24 22:35:47 -07:00
DefaultFeatures struct {
GitVersion * version . Version
2016-11-03 16:16:01 -06:00
2024-02-24 22:35:47 -07:00
SupportProcReceive bool // >= 2.29
SupportHashSha256 bool // >= 2.42, SHA-256 repositories no longer an ‘ experimental curiosity’
}
2022-06-09 19:57:49 -06:00
)
2020-09-05 10:42:58 -06:00
2024-02-14 10:18:30 -07:00
// loadGitVersion tries to get the current git version and stores it into a global variable
func loadGitVersion ( ) error {
2022-08-08 21:22:24 -06:00
// doesn't need RWMutex because it's executed by Init()
2024-02-24 22:35:47 -07:00
if DefaultFeatures . GitVersion != nil {
2024-02-14 10:18:30 -07:00
return nil
2016-11-03 16:16:01 -06:00
}
2022-06-09 19:57:49 -06:00
stdout , _ , runErr := NewCommand ( DefaultContext , "version" ) . RunStdString ( nil )
2022-03-31 20:55:30 -06:00
if runErr != nil {
2024-02-14 10:18:30 -07:00
return runErr
2016-11-03 16:16:01 -06:00
}
2024-02-14 10:18:30 -07:00
ver , err := parseGitVersionLine ( strings . TrimSpace ( stdout ) )
if err == nil {
2024-02-24 22:35:47 -07:00
DefaultFeatures . GitVersion = ver
2016-11-03 16:16:01 -06:00
}
2024-02-14 10:18:30 -07:00
return err
}
2016-11-03 16:16:01 -06:00
2024-02-14 10:18:30 -07:00
func parseGitVersionLine ( s string ) ( * version . Version , error ) {
fields := strings . Fields ( s )
if len ( fields ) < 3 {
return nil , fmt . Errorf ( "invalid git version: %q" , s )
2016-11-03 16:16:01 -06:00
}
2024-02-14 10:18:30 -07:00
// version string is like: "git version 2.29.3" or "git version 2.29.3.windows.1"
versionString := fields [ 2 ]
if pos := strings . Index ( versionString , "windows" ) ; pos >= 1 {
versionString = versionString [ : pos - 1 ]
}
return version . NewVersion ( versionString )
2016-11-03 16:16:01 -06:00
}
2019-07-07 01:26:56 -06:00
// SetExecutablePath changes the path of git executable and checks the file permission and version.
func SetExecutablePath ( path string ) error {
// If path is empty, we use the default value of GitExecutable "git" to search for the location of git.
if path != "" {
GitExecutable = path
}
2019-04-17 05:11:37 -06:00
absPath , err := exec . LookPath ( GitExecutable )
if err != nil {
2022-05-02 06:30:24 -06:00
return fmt . Errorf ( "git not found: %w" , err )
2019-04-17 05:11:37 -06:00
}
GitExecutable = absPath
2024-02-14 10:18:30 -07:00
if err = loadGitVersion ( ) ; err != nil {
2022-05-02 06:30:24 -06:00
return fmt . Errorf ( "unable to load git version: %w" , err )
2017-04-28 08:20:58 -06:00
}
2020-09-05 10:42:58 -06:00
2022-08-08 21:22:24 -06:00
versionRequired , err := version . NewVersion ( RequiredVersion )
2020-09-05 10:42:58 -06:00
if err != nil {
return err
}
2024-02-24 22:35:47 -07:00
if DefaultFeatures . GitVersion . LessThan ( versionRequired ) {
2022-05-02 06:30:24 -06:00
moreHint := "get git: https://git-scm.com/download/"
if runtime . GOOS == "linux" {
// there are a lot of CentOS/RHEL users using old git, so we add a special hint for them
if _ , err = os . Stat ( "/etc/redhat-release" ) ; err == nil {
// ius.io is the recommended official(git-scm.com) method to install git
moreHint = "get git: https://git-scm.com/download/linux and https://ius.io"
}
}
2024-02-24 22:35:47 -07:00
return fmt . Errorf ( "installed git version %q is not supported, Gitea requires git version >= %q, %s" , DefaultFeatures . GitVersion . Original ( ) , RequiredVersion , moreHint )
2017-04-28 08:20:58 -06:00
}
2019-07-07 01:26:56 -06:00
2024-02-24 22:35:47 -07:00
if err = checkGitVersionCompatibility ( DefaultFeatures . GitVersion ) ; err != nil {
return fmt . Errorf ( "installed git version %s has a known compatibility issue with Gitea: %w, please upgrade (or downgrade) git" , DefaultFeatures . GitVersion . String ( ) , err )
2024-02-14 10:18:30 -07:00
}
2019-07-07 01:26:56 -06:00
return nil
2019-06-19 10:53:37 -06:00
}
2019-05-14 19:57:00 -06:00
2021-06-26 05:28:55 -06:00
// VersionInfo returns git version information
func VersionInfo ( ) string {
2024-02-24 22:35:47 -07:00
if DefaultFeatures . GitVersion == nil {
2022-06-09 19:57:49 -06:00
return "(git not found)"
}
format := "%s"
2024-02-24 22:35:47 -07:00
args := [ ] any { DefaultFeatures . GitVersion . Original ( ) }
2021-06-26 05:28:55 -06:00
// Since git wire protocol has been released from git v2.18
if setting . Git . EnableAutoGitWireProtocol && CheckGitVersionAtLeast ( "2.18" ) == nil {
format += ", Wire Protocol %s Enabled"
args = append ( args , "Version 2" ) // for focus color
}
return fmt . Sprintf ( format , args ... )
}
2022-06-16 09:47:44 -06:00
func checkInit ( ) error {
2022-07-08 02:09:07 -06:00
if setting . Git . HomePath == "" {
return errors . New ( "unable to init Git's HomeDir, incorrect initialization of the setting and git modules" )
2022-06-16 09:47:44 -06:00
}
if DefaultContext != nil {
2022-08-08 21:22:24 -06:00
log . Warn ( "git module has been initialized already, duplicate init may work but it's better to fix it" )
2022-06-16 09:47:44 -06:00
}
return nil
}
2022-06-09 19:57:49 -06:00
// HomeDir is the home dir for git to store the global config file used by Gitea internally
func HomeDir ( ) string {
2022-07-08 02:09:07 -06:00
if setting . Git . HomePath == "" {
2022-06-16 09:47:44 -06:00
// strict check, make sure the git module is initialized correctly.
2022-08-08 21:22:24 -06:00
// attention: when the git module is called in gitea sub-command (serv/hook), the log module might not obviously show messages to users/developers.
2022-06-16 09:47:44 -06:00
// for example: if there is gitea git hook code calling git.NewCommand before git.InitXxx, the integration test won't show the real failure reasons.
2022-07-08 02:09:07 -06:00
log . Fatal ( "Unable to init Git's HomeDir, incorrect initialization of the setting and git modules" )
2022-06-16 09:47:44 -06:00
return ""
2022-06-09 19:57:49 -06:00
}
2022-07-08 02:09:07 -06:00
return setting . Git . HomePath
2022-06-09 19:57:49 -06:00
}
2022-06-10 21:56:27 -06:00
// InitSimple initializes git module with a very simple step, no config changes, no global command arguments.
2022-08-08 21:22:24 -06:00
// This method doesn't change anything to filesystem. At the moment, it is only used by some Gitea sub-commands.
2022-06-10 21:56:27 -06:00
func InitSimple ( ctx context . Context ) error {
2022-06-16 09:47:44 -06:00
if err := checkInit ( ) ; err != nil {
return err
}
2019-12-15 02:51:28 -07:00
DefaultContext = ctx
2022-08-08 21:22:24 -06:00
globalCommandArgs = nil
2020-09-05 10:42:58 -06:00
2022-03-31 05:56:22 -06:00
if setting . Git . Timeout . Default > 0 {
defaultCommandExecutionTimeout = time . Duration ( setting . Git . Timeout . Default ) * time . Second
}
2021-06-26 05:28:55 -06:00
2022-06-16 09:47:44 -06:00
return SetExecutablePath ( setting . Git . Path )
2022-06-09 19:57:49 -06:00
}
2022-08-08 21:22:24 -06:00
// InitFull initializes git module with version check and change global variables, sync gitconfig.
// It should only be called once at the beginning of the program initialization (TestMain/GlobalInitInstalled) as this code makes unsynchronized changes to variables.
func InitFull ( ctx context . Context ) ( err error ) {
if err = InitSimple ( ctx ) ; err != nil {
2023-07-09 05:58:06 -06:00
return err
2022-08-08 21:22:24 -06:00
}
2022-06-09 19:57:49 -06:00
2022-08-08 21:22:24 -06:00
// when git works with gnupg (commit signing), there should be a stable home for gnupg commands
if _ , ok := os . LookupEnv ( "GNUPGHOME" ) ; ! ok {
_ = os . Setenv ( "GNUPGHOME" , filepath . Join ( HomeDir ( ) , ".gnupg" ) )
}
2022-07-08 02:09:07 -06:00
2022-08-08 21:22:24 -06:00
// Since git wire protocol has been released from git v2.18
if setting . Git . EnableAutoGitWireProtocol && CheckGitVersionAtLeast ( "2.18" ) == nil {
globalCommandArgs = append ( globalCommandArgs , "-c" , "protocol.version=2" )
}
2022-06-10 21:56:27 -06:00
2022-08-08 21:22:24 -06:00
// Explicitly disable credential helper, otherwise Git credentials might leak
if CheckGitVersionAtLeast ( "2.9" ) == nil {
globalCommandArgs = append ( globalCommandArgs , "-c" , "credential.helper=" )
}
2024-02-24 22:35:47 -07:00
DefaultFeatures . SupportProcReceive = CheckGitVersionAtLeast ( "2.29" ) == nil
DefaultFeatures . SupportHashSha256 = CheckGitVersionAtLeast ( "2.42" ) == nil && ! isGogit
if DefaultFeatures . SupportHashSha256 {
2024-01-19 09:05:02 -07:00
SupportedObjectFormats = append ( SupportedObjectFormats , Sha256ObjectFormat )
} else {
log . Warn ( "sha256 hash support is disabled - requires Git >= 2.42. Gogit is currently unsupported" )
}
2022-08-08 21:22:24 -06:00
if setting . LFS . StartServer {
if CheckGitVersionAtLeast ( "2.1.2" ) != nil {
return errors . New ( "LFS server support requires Git >= 2.1.2" )
}
globalCommandArgs = append ( globalCommandArgs , "-c" , "filter.lfs.required=" , "-c" , "filter.lfs.smudge=" , "-c" , "filter.lfs.clean=" )
2021-06-26 05:28:55 -06:00
}
2022-08-08 21:22:24 -06:00
2022-06-10 21:56:27 -06:00
return syncGitConfig ( )
}
2021-06-26 05:28:55 -06:00
2022-06-10 21:56:27 -06:00
// syncGitConfig only modifies gitconfig, won't change global variables (otherwise there will be data-race problem)
func syncGitConfig ( ) ( err error ) {
if err = os . MkdirAll ( HomeDir ( ) , os . ModePerm ) ; err != nil {
2022-07-08 02:09:07 -06:00
return fmt . Errorf ( "unable to prepare git home directory %s, err: %w" , HomeDir ( ) , err )
2022-01-05 22:38:38 -07:00
}
2023-05-23 10:30:19 -06:00
// first, write user's git config options to git config file
// user config options could be overwritten by builtin values later, because if a value is builtin, it must have some special purposes
for k , v := range setting . GitConfig . Options {
if err = configSet ( strings . ToLower ( k ) , v ) ; err != nil {
return err
}
}
2022-06-09 19:57:49 -06:00
// Git requires setting user.name and user.email in order to commit changes - old comment: "if they're not set just add some defaults"
// TODO: need to confirm whether users really need to change these values manually. It seems that these values are dummy only and not really used.
// If these values are not really used, then they can be set (overwritten) directly without considering about existence.
for configKey , defaultValue := range map [ string ] string {
"user.name" : "Gitea" ,
"user.email" : "gitea@fake.local" ,
} {
if err := configSetNonExist ( configKey , defaultValue ) ; err != nil {
2020-06-13 15:47:31 -06:00
return err
2019-05-14 19:57:00 -06:00
}
}
2020-06-13 15:47:31 -06:00
// Set git some configurations - these must be set to these values for gitea to work correctly
2022-06-09 19:57:49 -06:00
if err := configSet ( "core.quotePath" , "false" ) ; err != nil {
2020-06-13 15:47:31 -06:00
return err
2019-05-14 19:57:00 -06:00
}
2019-06-29 05:46:25 -06:00
2020-10-21 09:42:08 -06:00
if CheckGitVersionAtLeast ( "2.10" ) == nil {
2022-06-09 19:57:49 -06:00
if err := configSet ( "receive.advertisePushOptions" , "true" ) ; err != nil {
2020-08-23 10:02:35 -06:00
return err
}
}
2020-10-21 09:42:08 -06:00
if CheckGitVersionAtLeast ( "2.18" ) == nil {
2022-06-09 19:57:49 -06:00
if err := configSet ( "core.commitGraph" , "true" ) ; err != nil {
2020-06-13 15:47:31 -06:00
return err
2019-06-29 05:46:25 -06:00
}
2022-06-09 19:57:49 -06:00
if err := configSet ( "gc.writeCommitGraph" , "true" ) ; err != nil {
2020-06-13 15:47:31 -06:00
return err
2019-06-29 05:46:25 -06:00
}
2022-06-17 14:18:35 -06:00
if err := configSet ( "fetch.writeCommitGraph" , "true" ) ; err != nil {
return err
}
2019-06-29 05:46:25 -06:00
}
2019-11-01 23:40:49 -06:00
2024-02-24 22:35:47 -07:00
if DefaultFeatures . SupportProcReceive {
2021-07-28 03:42:56 -06:00
// set support for AGit flow
2022-06-09 19:57:49 -06:00
if err := configAddNonExist ( "receive.procReceiveRefs" , "refs/for" ) ; err != nil {
2021-07-28 03:42:56 -06:00
return err
}
} else {
2022-06-09 19:57:49 -06:00
if err := configUnsetAll ( "receive.procReceiveRefs" , "refs/for" ) ; err != nil {
2021-07-28 03:42:56 -06:00
return err
}
}
2024-02-14 10:18:30 -07:00
// Due to CVE-2022-24765, git now denies access to git directories which are not owned by current user.
// However, some docker users and samba users find it difficult to configure their systems correctly,
// so that Gitea's git repositories are owned by the Gitea user.
// (Possibly Windows Service users - but ownership in this case should really be set correctly on the filesystem.)
// See issue: https://github.com/go-gitea/gitea/issues/19455
2022-06-16 23:49:38 -06:00
// As Gitea now always use its internal git config file, and access to the git repositories is managed through Gitea,
// it is now safe to set "safe.directory=*" for internal usage only.
2024-02-14 10:18:30 -07:00
// Although this setting is only supported by some new git versions, it is also tolerated by earlier versions
2022-06-16 23:49:38 -06:00
if err := configAddNonExist ( "safe.directory" , "*" ) ; err != nil {
return err
}
2024-02-14 10:18:30 -07:00
2019-11-01 23:40:49 -06:00
if runtime . GOOS == "windows" {
2022-06-09 19:57:49 -06:00
if err := configSet ( "core.longpaths" , "true" ) ; err != nil {
2020-06-13 15:47:31 -06:00
return err
}
2022-06-09 19:57:49 -06:00
if setting . Git . DisableCoreProtectNTFS {
err = configSet ( "core.protectNTFS" , "false" )
} else {
err = configUnsetAll ( "core.protectNTFS" , "false" )
}
if err != nil {
2021-10-13 12:20:11 -06:00
return err
}
}
2022-06-09 19:57:49 -06:00
2022-09-04 09:13:23 -06:00
// By default partial clones are disabled, enable them from git v2.22
if ! setting . Git . DisablePartialClone && CheckGitVersionAtLeast ( "2.22" ) == nil {
if err = configSet ( "uploadpack.allowfilter" , "true" ) ; err != nil {
return err
}
err = configSet ( "uploadpack.allowAnySHA1InWant" , "true" )
} else {
if err = configUnsetAll ( "uploadpack.allowfilter" , "true" ) ; err != nil {
return err
}
err = configUnsetAll ( "uploadpack.allowAnySHA1InWant" , "true" )
}
return err
2020-06-13 15:47:31 -06:00
}
2020-10-21 09:42:08 -06:00
// CheckGitVersionAtLeast check git version is at least the constraint version
func CheckGitVersionAtLeast ( atLeast string ) error {
2024-02-24 22:35:47 -07:00
if DefaultFeatures . GitVersion == nil {
2024-02-14 10:18:30 -07:00
panic ( "git module is not initialized" ) // it shouldn't happen
2020-09-05 10:42:58 -06:00
}
2020-10-21 09:42:08 -06:00
atLeastVersion , err := version . NewVersion ( atLeast )
2020-09-05 10:42:58 -06:00
if err != nil {
return err
}
2024-02-24 22:35:47 -07:00
if DefaultFeatures . GitVersion . Compare ( atLeastVersion ) < 0 {
return fmt . Errorf ( "installed git binary version %s is not at least %s" , DefaultFeatures . GitVersion . Original ( ) , atLeast )
2020-09-05 10:42:58 -06:00
}
return nil
}
2024-02-14 10:18:30 -07:00
func checkGitVersionCompatibility ( gitVer * version . Version ) error {
badVersions := [ ] struct {
Version * version . Version
Reason string
} {
{ version . Must ( version . NewVersion ( "2.43.1" ) ) , "regression bug of GIT_FLUSH" } ,
}
for _ , bad := range badVersions {
if gitVer . Equal ( bad . Version ) {
return errors . New ( bad . Reason )
}
}
return nil
}
2022-06-09 19:57:49 -06:00
func configSet ( key , value string ) error {
2023-02-28 14:26:19 -07:00
stdout , _ , err := NewCommand ( DefaultContext , "config" , "--global" , "--get" ) . AddDynamicArguments ( key ) . RunStdString ( nil )
2022-06-09 19:57:49 -06:00
if err != nil && ! err . IsExitCode ( 1 ) {
return fmt . Errorf ( "failed to get git config %s, err: %w" , key , err )
2020-06-13 15:47:31 -06:00
}
currValue := strings . TrimSpace ( stdout )
2022-06-09 19:57:49 -06:00
if currValue == value {
2020-06-13 15:47:31 -06:00
return nil
2019-11-01 23:40:49 -06:00
}
2020-06-13 15:47:31 -06:00
2022-10-23 08:44:45 -06:00
_ , _ , err = NewCommand ( DefaultContext , "config" , "--global" ) . AddDynamicArguments ( key , value ) . RunStdString ( nil )
2022-06-09 19:57:49 -06:00
if err != nil {
return fmt . Errorf ( "failed to set git global config %s, err: %w" , key , err )
2020-06-13 15:47:31 -06:00
}
2019-06-19 10:53:37 -06:00
return nil
2016-11-03 16:16:01 -06:00
}
2022-06-09 19:57:49 -06:00
func configSetNonExist ( key , value string ) error {
2023-02-28 14:26:19 -07:00
_ , _ , err := NewCommand ( DefaultContext , "config" , "--global" , "--get" ) . AddDynamicArguments ( key ) . RunStdString ( nil )
2022-06-09 19:57:49 -06:00
if err == nil {
// already exist
return nil
}
if err . IsExitCode ( 1 ) {
// not exist, set new config
2022-10-23 08:44:45 -06:00
_ , _ , err = NewCommand ( DefaultContext , "config" , "--global" ) . AddDynamicArguments ( key , value ) . RunStdString ( nil )
2022-06-09 19:57:49 -06:00
if err != nil {
return fmt . Errorf ( "failed to set git global config %s, err: %w" , key , err )
2021-07-28 03:42:56 -06:00
}
2022-06-09 19:57:49 -06:00
return nil
2021-07-28 03:42:56 -06:00
}
2022-06-09 19:57:49 -06:00
return fmt . Errorf ( "failed to get git config %s, err: %w" , key , err )
2021-07-28 03:42:56 -06:00
}
2022-06-09 19:57:49 -06:00
func configAddNonExist ( key , value string ) error {
2023-02-28 14:26:19 -07:00
_ , _ , err := NewCommand ( DefaultContext , "config" , "--global" , "--get" ) . AddDynamicArguments ( key , regexp . QuoteMeta ( value ) ) . RunStdString ( nil )
2022-06-09 19:57:49 -06:00
if err == nil {
// already exist
return nil
}
if err . IsExitCode ( 1 ) {
// not exist, add new config
2022-10-23 08:44:45 -06:00
_ , _ , err = NewCommand ( DefaultContext , "config" , "--global" , "--add" ) . AddDynamicArguments ( key , value ) . RunStdString ( nil )
2022-06-09 19:57:49 -06:00
if err != nil {
return fmt . Errorf ( "failed to add git global config %s, err: %w" , key , err )
2021-07-28 03:42:56 -06:00
}
2022-06-09 19:57:49 -06:00
return nil
2021-07-28 03:42:56 -06:00
}
2022-06-09 19:57:49 -06:00
return fmt . Errorf ( "failed to get git config %s, err: %w" , key , err )
}
2021-07-28 03:42:56 -06:00
2022-06-09 19:57:49 -06:00
func configUnsetAll ( key , value string ) error {
2023-02-28 14:26:19 -07:00
_ , _ , err := NewCommand ( DefaultContext , "config" , "--global" , "--get" ) . AddDynamicArguments ( key ) . RunStdString ( nil )
2022-06-09 19:57:49 -06:00
if err == nil {
// exist, need to remove
2022-10-23 08:44:45 -06:00
_ , _ , err = NewCommand ( DefaultContext , "config" , "--global" , "--unset-all" ) . AddDynamicArguments ( key , regexp . QuoteMeta ( value ) ) . RunStdString ( nil )
2022-06-09 19:57:49 -06:00
if err != nil {
return fmt . Errorf ( "failed to unset git global config %s, err: %w" , key , err )
}
return nil
2021-07-28 03:42:56 -06:00
}
2022-06-09 19:57:49 -06:00
if err . IsExitCode ( 1 ) {
// not exist
return nil
}
return fmt . Errorf ( "failed to get git config %s, err: %w" , key , err )
2021-07-28 03:42:56 -06:00
}
2016-11-03 16:16:01 -06:00
// Fsck verifies the connectivity and validity of the objects in the database
Refactor git command package to improve security and maintainability (#22678)
This PR follows #21535 (and replace #22592)
## Review without space diff
https://github.com/go-gitea/gitea/pull/22678/files?diff=split&w=1
## Purpose of this PR
1. Make git module command completely safe (risky user inputs won't be
passed as argument option anymore)
2. Avoid low-level mistakes like
https://github.com/go-gitea/gitea/pull/22098#discussion_r1045234918
3. Remove deprecated and dirty `CmdArgCheck` function, hide the `CmdArg`
type
4. Simplify code when using git command
## The main idea of this PR
* Move the `git.CmdArg` to the `internal` package, then no other package
except `git` could use it. Then developers could never do
`AddArguments(git.CmdArg(userInput))` any more.
* Introduce `git.ToTrustedCmdArgs`, it's for user-provided and already
trusted arguments. It's only used in a few cases, for example: use git
arguments from config file, help unit test with some arguments.
* Introduce `AddOptionValues` and `AddOptionFormat`, they make code more
clear and simple:
* Before: `AddArguments("-m").AddDynamicArguments(message)`
* After: `AddOptionValues("-m", message)`
* -
* Before: `AddArguments(git.CmdArg(fmt.Sprintf("--author='%s <%s>'",
sig.Name, sig.Email)))`
* After: `AddOptionFormat("--author='%s <%s>'", sig.Name, sig.Email)`
## FAQ
### Why these changes were not done in #21535 ?
#21535 is mainly a search&replace, it did its best to not change too
much logic.
Making the framework better needs a lot of changes, so this separate PR
is needed as the second step.
### The naming of `AddOptionXxx`
According to git's manual, the `--xxx` part is called `option`.
### How can it guarantee that `internal.CmdArg` won't be not misused?
Go's specification guarantees that. Trying to access other package's
internal package causes compilation error.
And, `golangci-lint` also denies the git/internal package. Only the
`git/command.go` can use it carefully.
### There is still a `ToTrustedCmdArgs`, will it still allow developers
to make mistakes and pass untrusted arguments?
Generally speaking, no. Because when using `ToTrustedCmdArgs`, the code
will be very complex (see the changes for examples). Then developers and
reviewers can know that something might be unreasonable.
### Why there was a `CmdArgCheck` and why it's removed?
At the moment of #21535, to reduce unnecessary changes, `CmdArgCheck`
was introduced as a hacky patch. Now, almost all code could be written
as `cmd := NewCommand(); cmd.AddXxx(...)`, then there is no need for
`CmdArgCheck` anymore.
### Why many codes for `signArg == ""` is deleted?
Because in the old code, `signArg` could never be empty string, it's
either `-S[key-id]` or `--no-gpg-sign`. So the `signArg == ""` is just
dead code.
---------
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2023-02-03 19:30:43 -07:00
func Fsck ( ctx context . Context , repoPath string , timeout time . Duration , args TrustedCmdArgs ) error {
2022-03-31 20:55:30 -06:00
return NewCommand ( ctx , "fsck" ) . AddArguments ( args ... ) . Run ( & RunOpts { Timeout : timeout , Dir : repoPath } )
2016-11-03 16:16:01 -06:00
}