Remove legacy git code (ver < 2.0), fine tune markup tests (#19930)
* clean git support for ver < 2.0 * fine tune tests for markup (which requires git module) * remove unnecessary comments * try to fix tests * try test again * use const for GitVersionRequired instead of var * try to fix integration test * Refactor CheckAttributeReader to make a *git.Repository version * update document for commit signing with Gitea's internal gitconfig * update document for commit signing with Gitea's internal gitconfig Co-authored-by: Andrew Thornton <art27@cantab.net> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
This commit is contained in:
parent
70ce051f1a
commit
157b405753
|
@ -308,6 +308,8 @@ func runHookPostReceive(c *cli.Context) error {
|
||||||
ctx, cancel := installSignals()
|
ctx, cancel := installSignals()
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
|
setup("hooks/post-receive.log", c.Bool("debug"))
|
||||||
|
|
||||||
// First of all run update-server-info no matter what
|
// First of all run update-server-info no matter what
|
||||||
if _, _, err := git.NewCommand(ctx, "update-server-info").RunStdString(nil); err != nil {
|
if _, _, err := git.NewCommand(ctx, "update-server-info").RunStdString(nil); err != nil {
|
||||||
return fmt.Errorf("Failed to call 'git update-server-info': %v", err)
|
return fmt.Errorf("Failed to call 'git update-server-info': %v", err)
|
||||||
|
@ -318,8 +320,6 @@ func runHookPostReceive(c *cli.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
setup("hooks/post-receive.log", c.Bool("debug"))
|
|
||||||
|
|
||||||
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
|
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
|
||||||
if setting.OnlyAllowPushIfGiteaEnvironmentSet {
|
if setting.OnlyAllowPushIfGiteaEnvironmentSet {
|
||||||
return fail(`Rejecting changes as Gitea environment not set.
|
return fail(`Rejecting changes as Gitea environment not set.
|
||||||
|
|
|
@ -83,8 +83,7 @@ The first option to discuss is the `SIGNING_KEY`. There are three main
|
||||||
options:
|
options:
|
||||||
|
|
||||||
- `none` - this prevents Gitea from signing any commits
|
- `none` - this prevents Gitea from signing any commits
|
||||||
- `default` - Gitea will default to the key configured within
|
- `default` - Gitea will default to the key configured within `git config`
|
||||||
`git config`
|
|
||||||
- `KEYID` - Gitea will sign commits with the gpg key with the ID
|
- `KEYID` - Gitea will sign commits with the gpg key with the ID
|
||||||
`KEYID`. In this case you should provide a `SIGNING_NAME` and
|
`KEYID`. In this case you should provide a `SIGNING_NAME` and
|
||||||
`SIGNING_EMAIL` to be displayed for this key.
|
`SIGNING_EMAIL` to be displayed for this key.
|
||||||
|
@ -98,6 +97,12 @@ repositories, `SIGNING_KEY=default` could be used to provide different
|
||||||
signing keys on a per-repository basis. However, this is clearly not an
|
signing keys on a per-repository basis. However, this is clearly not an
|
||||||
ideal UI and therefore subject to change.
|
ideal UI and therefore subject to change.
|
||||||
|
|
||||||
|
**Since 1.17**, Gitea runs git in its own home directory `[repository].ROOT` and uses its own config `{[repository].ROOT}/.gitconfig`.
|
||||||
|
If you have your own customized git config for Gitea, you should set these configs in system git config (aka `/etc/gitconfig`)
|
||||||
|
or the Gitea internal git config `{[repository].ROOT}/.gitconfig`.
|
||||||
|
Related home files for git command (like `.gnupg`) should also be put in Gitea's git home directory `[repository].ROOT`.
|
||||||
|
|
||||||
|
|
||||||
### `INITIAL_COMMIT`
|
### `INITIAL_COMMIT`
|
||||||
|
|
||||||
This option determines whether Gitea should sign the initial commit
|
This option determines whether Gitea should sign the initial commit
|
||||||
|
@ -118,7 +123,7 @@ The possible values are:
|
||||||
|
|
||||||
- `never`: Never sign
|
- `never`: Never sign
|
||||||
- `pubkey`: Only sign if the user has a public key
|
- `pubkey`: Only sign if the user has a public key
|
||||||
- `twofa`: Only sign if the user logs in with two factor authentication
|
- `twofa`: Only sign if the user logs in with two-factor authentication
|
||||||
- `parentsigned`: Only sign if the parent commit is signed.
|
- `parentsigned`: Only sign if the parent commit is signed.
|
||||||
- `always`: Always sign
|
- `always`: Always sign
|
||||||
|
|
||||||
|
@ -132,7 +137,7 @@ editor or API CRUD actions. The possible values are:
|
||||||
|
|
||||||
- `never`: Never sign
|
- `never`: Never sign
|
||||||
- `pubkey`: Only sign if the user has a public key
|
- `pubkey`: Only sign if the user has a public key
|
||||||
- `twofa`: Only sign if the user logs in with two factor authentication
|
- `twofa`: Only sign if the user logs in with two-factor authentication
|
||||||
- `parentsigned`: Only sign if the parent commit is signed.
|
- `parentsigned`: Only sign if the parent commit is signed.
|
||||||
- `always`: Always sign
|
- `always`: Always sign
|
||||||
|
|
||||||
|
@ -146,7 +151,7 @@ The possible options are:
|
||||||
|
|
||||||
- `never`: Never sign
|
- `never`: Never sign
|
||||||
- `pubkey`: Only sign if the user has a public key
|
- `pubkey`: Only sign if the user has a public key
|
||||||
- `twofa`: Only sign if the user logs in with two factor authentication
|
- `twofa`: Only sign if the user logs in with two-factor authentication
|
||||||
- `basesigned`: Only sign if the parent commit in the base repo is signed.
|
- `basesigned`: Only sign if the parent commit in the base repo is signed.
|
||||||
- `headsigned`: Only sign if the head commit in the head branch is signed.
|
- `headsigned`: Only sign if the head commit in the head branch is signed.
|
||||||
- `commitssigned`: Only sign if all the commits in the head branch to the merge point are signed.
|
- `commitssigned`: Only sign if all the commits in the head branch to the merge point are signed.
|
||||||
|
|
|
@ -174,7 +174,12 @@ func initIntegrationTest() {
|
||||||
setting.LoadForTest()
|
setting.LoadForTest()
|
||||||
setting.Repository.DefaultBranch = "master" // many test code still assume that default branch is called "master"
|
setting.Repository.DefaultBranch = "master" // many test code still assume that default branch is called "master"
|
||||||
_ = util.RemoveAll(repo_module.LocalCopyPath())
|
_ = util.RemoveAll(repo_module.LocalCopyPath())
|
||||||
|
|
||||||
|
if err := git.InitOnceWithSync(context.Background()); err != nil {
|
||||||
|
log.Fatal("git.InitOnceWithSync: %v", err)
|
||||||
|
}
|
||||||
git.CheckLFSVersion()
|
git.CheckLFSVersion()
|
||||||
|
|
||||||
setting.InitDBConfig()
|
setting.InitDBConfig()
|
||||||
if err := storage.Init(); err != nil {
|
if err := storage.Init(); err != nil {
|
||||||
fmt.Printf("Init storage failed: %v", err)
|
fmt.Printf("Init storage failed: %v", err)
|
||||||
|
@ -275,7 +280,7 @@ func prepareTestEnv(t testing.TB, skip ...int) func() {
|
||||||
assert.NoError(t, unittest.LoadFixtures())
|
assert.NoError(t, unittest.LoadFixtures())
|
||||||
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
|
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
|
||||||
assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
|
assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
|
||||||
assert.NoError(t, git.InitOnceWithSync(context.Background()))
|
assert.NoError(t, git.InitOnceWithSync(context.Background())) // the gitconfig has been removed above, so sync the gitconfig again
|
||||||
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
|
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
|
assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
|
||||||
|
@ -576,7 +581,7 @@ func resetFixtures(t *testing.T) {
|
||||||
assert.NoError(t, unittest.LoadFixtures())
|
assert.NoError(t, unittest.LoadFixtures())
|
||||||
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
|
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
|
||||||
assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
|
assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
|
||||||
assert.NoError(t, git.InitOnceWithSync(context.Background()))
|
assert.NoError(t, git.InitOnceWithSync(context.Background())) // the gitconfig has been removed above, so sync the gitconfig again
|
||||||
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
|
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
|
assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
|
||||||
|
|
|
@ -62,7 +62,6 @@ func initMigrationTest(t *testing.T) func() {
|
||||||
assert.True(t, len(setting.RepoRootPath) != 0)
|
assert.True(t, len(setting.RepoRootPath) != 0)
|
||||||
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
|
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
|
||||||
assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
|
assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
|
||||||
assert.NoError(t, git.InitOnceWithSync(context.Background()))
|
|
||||||
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
|
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
|
assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
|
||||||
|
@ -83,6 +82,7 @@ func initMigrationTest(t *testing.T) func() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert.NoError(t, git.InitOnceWithSync(context.Background()))
|
||||||
git.CheckLFSVersion()
|
git.CheckLFSVersion()
|
||||||
setting.InitDBConfig()
|
setting.InitDBConfig()
|
||||||
setting.NewLogServices(true)
|
setting.NewLogServices(true)
|
||||||
|
|
|
@ -66,6 +66,10 @@ func TestMain(m *testing.M) {
|
||||||
|
|
||||||
setting.SetCustomPathAndConf("", "", "")
|
setting.SetCustomPathAndConf("", "", "")
|
||||||
setting.LoadForTest()
|
setting.LoadForTest()
|
||||||
|
if err = git.InitOnceWithSync(context.Background()); err != nil {
|
||||||
|
fmt.Printf("Unable to InitOnceWithSync: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
git.CheckLFSVersion()
|
git.CheckLFSVersion()
|
||||||
setting.InitDBConfig()
|
setting.InitDBConfig()
|
||||||
setting.NewLogServices(true)
|
setting.NewLogServices(true)
|
||||||
|
@ -203,7 +207,7 @@ func prepareTestEnv(t *testing.T, skip int, syncModels ...interface{}) (*xorm.En
|
||||||
deferFn := PrintCurrentTest(t, ourSkip)
|
deferFn := PrintCurrentTest(t, ourSkip)
|
||||||
assert.NoError(t, os.RemoveAll(setting.RepoRootPath))
|
assert.NoError(t, os.RemoveAll(setting.RepoRootPath))
|
||||||
assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
|
assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
|
||||||
assert.NoError(t, git.InitOnceWithSync(context.Background()))
|
assert.NoError(t, git.InitOnceWithSync(context.Background())) // the gitconfig has been removed above, so sync the gitconfig again
|
||||||
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
|
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
|
assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
|
||||||
|
|
|
@ -117,9 +117,11 @@ func MainTest(m *testing.M, testOpts *TestOptions) {
|
||||||
if err = CopyDir(filepath.Join(testOpts.GiteaRootPath, "integrations", "gitea-repositories-meta"), setting.RepoRootPath); err != nil {
|
if err = CopyDir(filepath.Join(testOpts.GiteaRootPath, "integrations", "gitea-repositories-meta"), setting.RepoRootPath); err != nil {
|
||||||
fatalTestError("util.CopyDir: %v\n", err)
|
fatalTestError("util.CopyDir: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = git.InitOnceWithSync(context.Background()); err != nil {
|
if err = git.InitOnceWithSync(context.Background()); err != nil {
|
||||||
fatalTestError("git.Init: %v\n", err)
|
fatalTestError("git.Init: %v\n", err)
|
||||||
}
|
}
|
||||||
|
git.CheckLFSVersion()
|
||||||
|
|
||||||
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
|
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -202,7 +204,7 @@ func PrepareTestEnv(t testing.TB) {
|
||||||
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
|
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
|
||||||
metaPath := filepath.Join(giteaRoot, "integrations", "gitea-repositories-meta")
|
metaPath := filepath.Join(giteaRoot, "integrations", "gitea-repositories-meta")
|
||||||
assert.NoError(t, CopyDir(metaPath, setting.RepoRootPath))
|
assert.NoError(t, CopyDir(metaPath, setting.RepoRootPath))
|
||||||
assert.NoError(t, git.InitOnceWithSync(context.Background()))
|
assert.NoError(t, git.InitOnceWithSync(context.Background())) // the gitconfig has been removed above, so sync the gitconfig again
|
||||||
|
|
||||||
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
|
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
|
@ -206,7 +206,6 @@ func (c *Commit) HasPreviousCommit(commitHash SHA1) (bool, error) {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := CheckGitVersionAtLeast("1.8"); err == nil {
|
|
||||||
_, _, err := NewCommand(c.repo.Ctx, "merge-base", "--is-ancestor", that, this).RunStdString(&RunOpts{Dir: c.repo.Path})
|
_, _, err := NewCommand(c.repo.Ctx, "merge-base", "--is-ancestor", that, this).RunStdString(&RunOpts{Dir: c.repo.Path})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return true, nil
|
return true, nil
|
||||||
|
@ -218,14 +217,6 @@ func (c *Commit) HasPreviousCommit(commitHash SHA1) (bool, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false, err
|
return false, err
|
||||||
}
|
|
||||||
|
|
||||||
result, _, err := NewCommand(c.repo.Ctx, "rev-list", "--ancestry-path", "-n1", that+".."+this, "--").RunStdString(&RunOpts{Dir: c.repo.Path})
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return len(strings.TrimSpace(result)) > 0, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CommitsBeforeLimit returns num commits before current revision
|
// CommitsBeforeLimit returns num commits before current revision
|
||||||
|
|
|
@ -7,10 +7,10 @@ package git
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -22,20 +22,16 @@ import (
|
||||||
"github.com/hashicorp/go-version"
|
"github.com/hashicorp/go-version"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
// GitVersionRequired is the minimum Git version required
|
||||||
// GitVersionRequired is the minimum Git version required
|
const GitVersionRequired = "2.0.0"
|
||||||
// At the moment, all code for git 1.x are not changed, if some users want to test with old git client
|
|
||||||
// or bypass the check, they still have a chance to edit this variable manually.
|
|
||||||
// If everything works fine, the code for git 1.x could be removed in a separate PR before 1.17 frozen.
|
|
||||||
GitVersionRequired = "2.0.0"
|
|
||||||
|
|
||||||
|
var (
|
||||||
// GitExecutable is the command name of git
|
// GitExecutable is the command name of git
|
||||||
// Could be updated to an absolute path while initialization
|
// Could be updated to an absolute path while initialization
|
||||||
GitExecutable = "git"
|
GitExecutable = "git"
|
||||||
|
|
||||||
// DefaultContext is the default context to run git commands in
|
// DefaultContext is the default context to run git commands in, must be initialized by git.InitXxx
|
||||||
// will be overwritten by InitXxx with HammerContext
|
DefaultContext context.Context
|
||||||
DefaultContext = context.Background()
|
|
||||||
|
|
||||||
// SupportProcReceive version >= 2.29.0
|
// SupportProcReceive version >= 2.29.0
|
||||||
SupportProcReceive bool
|
SupportProcReceive bool
|
||||||
|
@ -128,36 +124,43 @@ func VersionInfo() string {
|
||||||
return fmt.Sprintf(format, args...)
|
return fmt.Sprintf(format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func checkInit() error {
|
||||||
|
if setting.RepoRootPath == "" {
|
||||||
|
return errors.New("can not init Git's HomeDir (RepoRootPath is empty), the setting and git modules are not initialized correctly")
|
||||||
|
}
|
||||||
|
if DefaultContext != nil {
|
||||||
|
log.Warn("git module has been initialized already, duplicate init should be fixed")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// HomeDir is the home dir for git to store the global config file used by Gitea internally
|
// HomeDir is the home dir for git to store the global config file used by Gitea internally
|
||||||
func HomeDir() string {
|
func HomeDir() string {
|
||||||
if setting.RepoRootPath == "" {
|
if setting.RepoRootPath == "" {
|
||||||
// TODO: now, some unit test code call the git module directly without initialization, which is incorrect.
|
// strict check, make sure the git module is initialized correctly.
|
||||||
// at the moment, we just use a temp HomeDir to prevent from conflicting with user's git config
|
// attention: when the git module is called in gitea sub-command (serv/hook), the log module is not able to show messages to users.
|
||||||
// in the future, the git module should be initialized first before use.
|
// 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.
|
||||||
tmpHomeDir := filepath.Join(os.TempDir(), "gitea-temp-home")
|
log.Fatal("can not get Git's HomeDir (RepoRootPath is empty), the setting and git modules are not initialized correctly")
|
||||||
log.Error("Git's HomeDir is empty (RepoRootPath is empty), the git module is not initialized correctly, using a temp HomeDir (%s) temporarily", tmpHomeDir)
|
return ""
|
||||||
return tmpHomeDir
|
|
||||||
}
|
}
|
||||||
return setting.RepoRootPath
|
return setting.RepoRootPath
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitSimple initializes git module with a very simple step, no config changes, no global command arguments.
|
// InitSimple initializes git module with a very simple step, no config changes, no global command arguments.
|
||||||
// This method doesn't change anything to filesystem. At the moment, it is only used by "git serv" sub-command, no data-race
|
// This method doesn't change anything to filesystem. At the moment, it is only used by "git serv" sub-command, no data-race
|
||||||
|
// However, in integration test, the sub-command function may be called in the current process, so the InitSimple would be called multiple times, too
|
||||||
func InitSimple(ctx context.Context) error {
|
func InitSimple(ctx context.Context) error {
|
||||||
|
if err := checkInit(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
DefaultContext = ctx
|
DefaultContext = ctx
|
||||||
|
|
||||||
if setting.Git.Timeout.Default > 0 {
|
if setting.Git.Timeout.Default > 0 {
|
||||||
defaultCommandExecutionTimeout = time.Duration(setting.Git.Timeout.Default) * time.Second
|
defaultCommandExecutionTimeout = time.Duration(setting.Git.Timeout.Default) * time.Second
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := SetExecutablePath(setting.Git.Path); err != nil {
|
return SetExecutablePath(setting.Git.Path)
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// force cleanup args
|
|
||||||
globalCommandArgs = []string{}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var initOnce sync.Once
|
var initOnce sync.Once
|
||||||
|
@ -166,6 +169,10 @@ var initOnce sync.Once
|
||||||
// This method will update the global variables ONLY ONCE (just like git.CheckLFSVersion -- which is not ideal too),
|
// This method will update the global variables ONLY ONCE (just like git.CheckLFSVersion -- which is not ideal too),
|
||||||
// otherwise there will be data-race problem at the moment.
|
// otherwise there will be data-race problem at the moment.
|
||||||
func InitOnceWithSync(ctx context.Context) (err error) {
|
func InitOnceWithSync(ctx context.Context) (err error) {
|
||||||
|
if err = checkInit(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
initOnce.Do(func() {
|
initOnce.Do(func() {
|
||||||
err = InitSimple(ctx)
|
err = InitSimple(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -30,10 +30,10 @@ type CheckAttributeOpts struct {
|
||||||
func (repo *Repository) CheckAttribute(opts CheckAttributeOpts) (map[string]map[string]string, error) {
|
func (repo *Repository) CheckAttribute(opts CheckAttributeOpts) (map[string]map[string]string, error) {
|
||||||
env := []string{}
|
env := []string{}
|
||||||
|
|
||||||
if len(opts.IndexFile) > 0 && CheckGitVersionAtLeast("1.7.8") == nil {
|
if len(opts.IndexFile) > 0 {
|
||||||
env = append(env, "GIT_INDEX_FILE="+opts.IndexFile)
|
env = append(env, "GIT_INDEX_FILE="+opts.IndexFile)
|
||||||
}
|
}
|
||||||
if len(opts.WorkTree) > 0 && CheckGitVersionAtLeast("1.7.8") == nil {
|
if len(opts.WorkTree) > 0 {
|
||||||
env = append(env, "GIT_WORK_TREE="+opts.WorkTree)
|
env = append(env, "GIT_WORK_TREE="+opts.WorkTree)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,8 +56,7 @@ func (repo *Repository) CheckAttribute(opts CheckAttributeOpts) (map[string]map[
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// git check-attr --cached first appears in git 1.7.8
|
if opts.CachedOnly {
|
||||||
if opts.CachedOnly && CheckGitVersionAtLeast("1.7.8") == nil {
|
|
||||||
cmdArgs = append(cmdArgs, "--cached")
|
cmdArgs = append(cmdArgs, "--cached")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,12 +124,12 @@ type CheckAttributeReader struct {
|
||||||
func (c *CheckAttributeReader) Init(ctx context.Context) error {
|
func (c *CheckAttributeReader) Init(ctx context.Context) error {
|
||||||
cmdArgs := []string{"check-attr", "--stdin", "-z"}
|
cmdArgs := []string{"check-attr", "--stdin", "-z"}
|
||||||
|
|
||||||
if len(c.IndexFile) > 0 && CheckGitVersionAtLeast("1.7.8") == nil {
|
if len(c.IndexFile) > 0 {
|
||||||
cmdArgs = append(cmdArgs, "--cached")
|
cmdArgs = append(cmdArgs, "--cached")
|
||||||
c.env = append(c.env, "GIT_INDEX_FILE="+c.IndexFile)
|
c.env = append(c.env, "GIT_INDEX_FILE="+c.IndexFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(c.WorkTree) > 0 && CheckGitVersionAtLeast("1.7.8") == nil {
|
if len(c.WorkTree) > 0 {
|
||||||
c.env = append(c.env, "GIT_WORK_TREE="+c.WorkTree)
|
c.env = append(c.env, "GIT_WORK_TREE="+c.WorkTree)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,17 +159,10 @@ func (c *CheckAttributeReader) Init(ctx context.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if CheckGitVersionAtLeast("1.8.5") == nil {
|
|
||||||
lw := new(nulSeparatedAttributeWriter)
|
lw := new(nulSeparatedAttributeWriter)
|
||||||
lw.attributes = make(chan attributeTriple, 5)
|
lw.attributes = make(chan attributeTriple, 5)
|
||||||
lw.closed = make(chan struct{})
|
lw.closed = make(chan struct{})
|
||||||
c.stdOut = lw
|
c.stdOut = lw
|
||||||
} else {
|
|
||||||
lw := new(lineSeparatedAttributeWriter)
|
|
||||||
lw.attributes = make(chan attributeTriple, 5)
|
|
||||||
lw.closed = make(chan struct{})
|
|
||||||
c.stdOut = lw
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -400,3 +392,37 @@ func (wr *lineSeparatedAttributeWriter) Close() error {
|
||||||
close(wr.closed)
|
close(wr.closed)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a check attribute reader for the current repository and provided commit ID
|
||||||
|
func (repo *Repository) CheckAttributeReader(commitID string) (*CheckAttributeReader, context.CancelFunc) {
|
||||||
|
indexFilename, worktree, deleteTemporaryFile, err := repo.ReadTreeToTemporaryIndex(commitID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, func() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
checker := &CheckAttributeReader{
|
||||||
|
Attributes: []string{"linguist-vendored", "linguist-generated", "linguist-language", "gitlab-language"},
|
||||||
|
Repo: repo,
|
||||||
|
IndexFile: indexFilename,
|
||||||
|
WorkTree: worktree,
|
||||||
|
}
|
||||||
|
ctx, cancel := context.WithCancel(repo.Ctx)
|
||||||
|
if err := checker.Init(ctx); err != nil {
|
||||||
|
log.Error("Unable to open checker for %s. Error: %v", commitID, err)
|
||||||
|
} else {
|
||||||
|
go func() {
|
||||||
|
err := checker.Run()
|
||||||
|
if err != nil && err != ctx.Err() {
|
||||||
|
log.Error("Unable to open checker for %s. Error: %v", commitID, err)
|
||||||
|
}
|
||||||
|
cancel()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
deferable := func() {
|
||||||
|
_ = checker.Close()
|
||||||
|
cancel()
|
||||||
|
deleteTemporaryFile()
|
||||||
|
}
|
||||||
|
|
||||||
|
return checker, deferable
|
||||||
|
}
|
||||||
|
|
|
@ -255,16 +255,10 @@ func (repo *Repository) GetDiff(base, head string, w io.Writer) error {
|
||||||
|
|
||||||
// GetDiffBinary generates and returns patch data between given revisions, including binary diffs.
|
// GetDiffBinary generates and returns patch data between given revisions, including binary diffs.
|
||||||
func (repo *Repository) GetDiffBinary(base, head string, w io.Writer) error {
|
func (repo *Repository) GetDiffBinary(base, head string, w io.Writer) error {
|
||||||
if CheckGitVersionAtLeast("1.7.7") == nil {
|
|
||||||
return NewCommand(repo.Ctx, "diff", "-p", "--binary", "--histogram", base, head).Run(&RunOpts{
|
return NewCommand(repo.Ctx, "diff", "-p", "--binary", "--histogram", base, head).Run(&RunOpts{
|
||||||
Dir: repo.Path,
|
Dir: repo.Path,
|
||||||
Stdout: w,
|
Stdout: w,
|
||||||
})
|
})
|
||||||
}
|
|
||||||
return NewCommand(repo.Ctx, "diff", "-p", "--binary", "--patience", base, head).Run(&RunOpts{
|
|
||||||
Dir: repo.Path,
|
|
||||||
Stdout: w,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPatch generates and returns format-patch data between given revisions, able to be used with `git apply`
|
// GetPatch generates and returns format-patch data between given revisions, able to be used with `git apply`
|
||||||
|
|
|
@ -8,12 +8,10 @@ package git
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
|
||||||
"io"
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/analyze"
|
"code.gitea.io/gitea/modules/analyze"
|
||||||
"code.gitea.io/gitea/modules/log"
|
|
||||||
|
|
||||||
"github.com/go-enry/go-enry/v2"
|
"github.com/go-enry/go-enry/v2"
|
||||||
"github.com/go-git/go-git/v5"
|
"github.com/go-git/go-git/v5"
|
||||||
|
@ -43,33 +41,8 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var checker *CheckAttributeReader
|
checker, deferable := repo.CheckAttributeReader(commitID)
|
||||||
|
defer deferable()
|
||||||
if CheckGitVersionAtLeast("1.7.8") == nil {
|
|
||||||
indexFilename, workTree, deleteTemporaryFile, err := repo.ReadTreeToTemporaryIndex(commitID)
|
|
||||||
if err == nil {
|
|
||||||
defer deleteTemporaryFile()
|
|
||||||
checker = &CheckAttributeReader{
|
|
||||||
Attributes: []string{"linguist-vendored", "linguist-generated", "linguist-language", "gitlab-language"},
|
|
||||||
Repo: repo,
|
|
||||||
IndexFile: indexFilename,
|
|
||||||
WorkTree: workTree,
|
|
||||||
}
|
|
||||||
ctx, cancel := context.WithCancel(DefaultContext)
|
|
||||||
if err := checker.Init(ctx); err != nil {
|
|
||||||
log.Error("Unable to open checker for %s. Error: %v", commitID, err)
|
|
||||||
} else {
|
|
||||||
go func() {
|
|
||||||
err = checker.Run()
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Unable to open checker for %s. Error: %v", commitID, err)
|
|
||||||
cancel()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
defer cancel()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sizes := make(map[string]int64)
|
sizes := make(map[string]int64)
|
||||||
err = tree.Files().ForEach(func(f *object.File) error {
|
err = tree.Files().ForEach(func(f *object.File) error {
|
||||||
|
|
|
@ -9,7 +9,6 @@ package git
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -63,36 +62,8 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var checker *CheckAttributeReader
|
checker, deferable := repo.CheckAttributeReader(commitID)
|
||||||
|
defer deferable()
|
||||||
if CheckGitVersionAtLeast("1.7.8") == nil {
|
|
||||||
indexFilename, worktree, deleteTemporaryFile, err := repo.ReadTreeToTemporaryIndex(commitID)
|
|
||||||
if err == nil {
|
|
||||||
defer deleteTemporaryFile()
|
|
||||||
checker = &CheckAttributeReader{
|
|
||||||
Attributes: []string{"linguist-vendored", "linguist-generated", "linguist-language", "gitlab-language"},
|
|
||||||
Repo: repo,
|
|
||||||
IndexFile: indexFilename,
|
|
||||||
WorkTree: worktree,
|
|
||||||
}
|
|
||||||
ctx, cancel := context.WithCancel(repo.Ctx)
|
|
||||||
if err := checker.Init(ctx); err != nil {
|
|
||||||
log.Error("Unable to open checker for %s. Error: %v", commitID, err)
|
|
||||||
} else {
|
|
||||||
go func() {
|
|
||||||
err = checker.Run()
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Unable to open checker for %s. Error: %v", commitID, err)
|
|
||||||
cancel()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
_ = checker.Close()
|
|
||||||
cancel()
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
contentBuf := bytes.Buffer{}
|
contentBuf := bytes.Buffer{}
|
||||||
var content []byte
|
var content []byte
|
||||||
|
|
|
@ -45,11 +45,11 @@ func (repo *Repository) CommitTree(author, committer *Signature, tree *Tree, opt
|
||||||
_, _ = messageBytes.WriteString(opts.Message)
|
_, _ = messageBytes.WriteString(opts.Message)
|
||||||
_, _ = messageBytes.WriteString("\n")
|
_, _ = messageBytes.WriteString("\n")
|
||||||
|
|
||||||
if CheckGitVersionAtLeast("1.7.9") == nil && (opts.KeyID != "" || opts.AlwaysSign) {
|
if opts.KeyID != "" || opts.AlwaysSign {
|
||||||
cmd.AddArguments(fmt.Sprintf("-S%s", opts.KeyID))
|
cmd.AddArguments(fmt.Sprintf("-S%s", opts.KeyID))
|
||||||
}
|
}
|
||||||
|
|
||||||
if CheckGitVersionAtLeast("2.0.0") == nil && opts.NoGPGSign {
|
if opts.NoGPGSign {
|
||||||
cmd.AddArguments("--no-gpg-sign")
|
cmd.AddArguments("--no-gpg-sign")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,12 +5,14 @@
|
||||||
package markup_test
|
package markup_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"io"
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/emoji"
|
"code.gitea.io/gitea/modules/emoji"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
. "code.gitea.io/gitea/modules/markup"
|
. "code.gitea.io/gitea/modules/markup"
|
||||||
"code.gitea.io/gitea/modules/markup/markdown"
|
"code.gitea.io/gitea/modules/markup/markdown"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
@ -25,6 +27,13 @@ var localMetas = map[string]string{
|
||||||
"repoPath": "../../integrations/gitea-repositories-meta/user13/repo11.git/",
|
"repoPath": "../../integrations/gitea-repositories-meta/user13/repo11.git/",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
setting.LoadAllowEmpty()
|
||||||
|
if err := git.InitSimple(context.Background()); err != nil {
|
||||||
|
log.Fatal("git init failed, err: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestRender_Commits(t *testing.T) {
|
func TestRender_Commits(t *testing.T) {
|
||||||
setting.AppURL = TestAppURL
|
setting.AppURL = TestAppURL
|
||||||
test := func(input, expected string) {
|
test := func(input, expected string) {
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
package markdown_test
|
package markdown_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -31,6 +32,13 @@ var localMetas = map[string]string{
|
||||||
"repoPath": "../../../integrations/gitea-repositories-meta/user13/repo11.git/",
|
"repoPath": "../../../integrations/gitea-repositories-meta/user13/repo11.git/",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
setting.LoadAllowEmpty()
|
||||||
|
if err := git.InitSimple(context.Background()); err != nil {
|
||||||
|
log.Fatal("git init failed, err: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestRender_StandardLinks(t *testing.T) {
|
func TestRender_StandardLinks(t *testing.T) {
|
||||||
setting.AppURL = AppURL
|
setting.AppURL = AppURL
|
||||||
setting.AppSubURL = AppSubURL
|
setting.AppSubURL = AppSubURL
|
||||||
|
|
|
@ -323,7 +323,6 @@ func initRepoCommit(ctx context.Context, tmpPath string, repo *repo_model.Reposi
|
||||||
"-m", "Initial commit",
|
"-m", "Initial commit",
|
||||||
}
|
}
|
||||||
|
|
||||||
if git.CheckGitVersionAtLeast("1.7.9") == nil {
|
|
||||||
sign, keyID, signer, _ := asymkey_service.SignInitialCommit(ctx, tmpPath, u)
|
sign, keyID, signer, _ := asymkey_service.SignInitialCommit(ctx, tmpPath, u)
|
||||||
if sign {
|
if sign {
|
||||||
args = append(args, "-S"+keyID)
|
args = append(args, "-S"+keyID)
|
||||||
|
@ -333,10 +332,9 @@ func initRepoCommit(ctx context.Context, tmpPath string, repo *repo_model.Reposi
|
||||||
committerName = signer.Name
|
committerName = signer.Name
|
||||||
committerEmail = signer.Email
|
committerEmail = signer.Email
|
||||||
}
|
}
|
||||||
} else if git.CheckGitVersionAtLeast("2.0.0") == nil {
|
} else {
|
||||||
args = append(args, "--no-gpg-sign")
|
args = append(args, "--no-gpg-sign")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
env = append(env,
|
env = append(env,
|
||||||
"GIT_COMMITTER_NAME="+committerName,
|
"GIT_COMMITTER_NAME="+committerName,
|
||||||
|
|
|
@ -1417,37 +1417,8 @@ func GetDiff(gitRepo *git.Repository, opts *DiffOptions, files ...string) (*Diff
|
||||||
}
|
}
|
||||||
diff.Start = opts.SkipTo
|
diff.Start = opts.SkipTo
|
||||||
|
|
||||||
var checker *git.CheckAttributeReader
|
checker, deferable := gitRepo.CheckAttributeReader(opts.AfterCommitID)
|
||||||
|
defer deferable()
|
||||||
if git.CheckGitVersionAtLeast("1.7.8") == nil {
|
|
||||||
indexFilename, worktree, deleteTemporaryFile, err := gitRepo.ReadTreeToTemporaryIndex(opts.AfterCommitID)
|
|
||||||
if err == nil {
|
|
||||||
defer deleteTemporaryFile()
|
|
||||||
|
|
||||||
checker = &git.CheckAttributeReader{
|
|
||||||
Attributes: []string{"linguist-vendored", "linguist-generated", "linguist-language", "gitlab-language"},
|
|
||||||
Repo: gitRepo,
|
|
||||||
IndexFile: indexFilename,
|
|
||||||
WorkTree: worktree,
|
|
||||||
}
|
|
||||||
ctx, cancel := context.WithCancel(gitRepo.Ctx)
|
|
||||||
if err := checker.Init(ctx); err != nil {
|
|
||||||
log.Error("Unable to open checker for %s. Error: %v", opts.AfterCommitID, err)
|
|
||||||
} else {
|
|
||||||
go func() {
|
|
||||||
err := checker.Run()
|
|
||||||
if err != nil && err != ctx.Err() {
|
|
||||||
log.Error("Unable to open checker for %s. Error: %v", opts.AfterCommitID, err)
|
|
||||||
}
|
|
||||||
cancel()
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
_ = checker.Close()
|
|
||||||
cancel()
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, diffFile := range diff.Files {
|
for _, diffFile := range diff.Files {
|
||||||
|
|
||||||
|
|
|
@ -276,16 +276,9 @@ func rawMerge(ctx context.Context, pr *issues_model.PullRequest, doer *user_mode
|
||||||
return "", fmt.Errorf("Unable to write .git/info/sparse-checkout file in tmpBasePath: %v", err)
|
return "", fmt.Errorf("Unable to write .git/info/sparse-checkout file in tmpBasePath: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var gitConfigCommand func() *git.Command
|
gitConfigCommand := func() *git.Command {
|
||||||
if git.CheckGitVersionAtLeast("1.8.0") == nil {
|
|
||||||
gitConfigCommand = func() *git.Command {
|
|
||||||
return git.NewCommand(ctx, "config", "--local")
|
return git.NewCommand(ctx, "config", "--local")
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
gitConfigCommand = func() *git.Command {
|
|
||||||
return git.NewCommand(ctx, "config")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Switch off LFS process (set required, clean and smudge here also)
|
// Switch off LFS process (set required, clean and smudge here also)
|
||||||
if err := gitConfigCommand().AddArguments("filter.lfs.process", "").
|
if err := gitConfigCommand().AddArguments("filter.lfs.process", "").
|
||||||
|
@ -366,17 +359,15 @@ func rawMerge(ctx context.Context, pr *issues_model.PullRequest, doer *user_mode
|
||||||
|
|
||||||
// Determine if we should sign
|
// Determine if we should sign
|
||||||
signArg := ""
|
signArg := ""
|
||||||
if git.CheckGitVersionAtLeast("1.7.9") == nil {
|
|
||||||
sign, keyID, signer, _ := asymkey_service.SignMerge(ctx, pr, doer, tmpBasePath, "HEAD", trackingBranch)
|
sign, keyID, signer, _ := asymkey_service.SignMerge(ctx, pr, doer, tmpBasePath, "HEAD", trackingBranch)
|
||||||
if sign {
|
if sign {
|
||||||
signArg = "-S" + keyID
|
signArg = "-S" + keyID
|
||||||
if pr.BaseRepo.GetTrustModel() == repo_model.CommitterTrustModel || pr.BaseRepo.GetTrustModel() == repo_model.CollaboratorCommitterTrustModel {
|
if pr.BaseRepo.GetTrustModel() == repo_model.CommitterTrustModel || pr.BaseRepo.GetTrustModel() == repo_model.CollaboratorCommitterTrustModel {
|
||||||
committer = signer
|
committer = signer
|
||||||
}
|
}
|
||||||
} else if git.CheckGitVersionAtLeast("2.0.0") == nil {
|
} else {
|
||||||
signArg = "--no-gpg-sign"
|
signArg = "--no-gpg-sign"
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
commitTimeStr := time.Now().Format(time.RFC3339)
|
commitTimeStr := time.Now().Format(time.RFC3339)
|
||||||
|
|
||||||
|
|
|
@ -248,8 +248,6 @@ func (t *TemporaryUploadRepository) CommitTreeWithDate(parent string, author, co
|
||||||
args = []string{"commit-tree", treeHash}
|
args = []string{"commit-tree", treeHash}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine if we should sign
|
|
||||||
if git.CheckGitVersionAtLeast("1.7.9") == nil {
|
|
||||||
var sign bool
|
var sign bool
|
||||||
var keyID string
|
var keyID string
|
||||||
var signer *git.Signature
|
var signer *git.Signature
|
||||||
|
@ -273,10 +271,9 @@ func (t *TemporaryUploadRepository) CommitTreeWithDate(parent string, author, co
|
||||||
}
|
}
|
||||||
committerSig = signer
|
committerSig = signer
|
||||||
}
|
}
|
||||||
} else if git.CheckGitVersionAtLeast("2.0.0") == nil {
|
} else {
|
||||||
args = append(args, "--no-gpg-sign")
|
args = append(args, "--no-gpg-sign")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if signoff {
|
if signoff {
|
||||||
// Signed-off-by
|
// Signed-off-by
|
||||||
|
|
Loading…
Reference in New Issue