diff --git a/cmd/hook.go b/cmd/hook.go index aabd9637c2..331f6a2d2d 100644 --- a/cmd/hook.go +++ b/cmd/hook.go @@ -8,10 +8,12 @@ import ( "bufio" "bytes" "fmt" + "io" "net/http" "os" "strconv" "strings" + "time" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/git" @@ -58,6 +60,85 @@ var ( } ) +type delayWriter struct { + internal io.Writer + buf *bytes.Buffer + timer *time.Timer +} + +func newDelayWriter(internal io.Writer, delay time.Duration) *delayWriter { + timer := time.NewTimer(delay) + return &delayWriter{ + internal: internal, + buf: &bytes.Buffer{}, + timer: timer, + } +} + +func (d *delayWriter) Write(p []byte) (n int, err error) { + if d.buf != nil { + select { + case <-d.timer.C: + _, err := d.internal.Write(d.buf.Bytes()) + if err != nil { + return 0, err + } + d.buf = nil + return d.internal.Write(p) + default: + return d.buf.Write(p) + } + } + return d.internal.Write(p) +} + +func (d *delayWriter) WriteString(s string) (n int, err error) { + if d.buf != nil { + select { + case <-d.timer.C: + _, err := d.internal.Write(d.buf.Bytes()) + if err != nil { + return 0, err + } + d.buf = nil + return d.internal.Write([]byte(s)) + default: + return d.buf.WriteString(s) + } + } + return d.internal.Write([]byte(s)) +} + +func (d *delayWriter) Close() error { + if d == nil { + return nil + } + stopped := d.timer.Stop() + if stopped { + return nil + } + select { + case <-d.timer.C: + default: + } + if d.buf == nil { + return nil + } + _, err := d.internal.Write(d.buf.Bytes()) + d.buf = nil + return err +} + +type nilWriter struct{} + +func (n *nilWriter) Write(p []byte) (int, error) { + return len(p), nil +} + +func (n *nilWriter) WriteString(s string) (int, error) { + return len(s), nil +} + func runHookPreReceive(c *cli.Context) error { if os.Getenv(models.EnvIsInternal) == "true" { return nil @@ -101,6 +182,18 @@ Gitea or set your environment appropriately.`, "") total := 0 lastline := 0 + var out io.Writer + out = &nilWriter{} + if setting.Git.VerbosePush { + if setting.Git.VerbosePushDelay > 0 { + dWriter := newDelayWriter(os.Stdout, setting.Git.VerbosePushDelay) + defer dWriter.Close() + out = dWriter + } else { + out = os.Stdout + } + } + for scanner.Scan() { // TODO: support news feeds for wiki if isWiki { @@ -124,12 +217,10 @@ Gitea or set your environment appropriately.`, "") newCommitIDs[count] = newCommitID refFullNames[count] = refFullName count++ - fmt.Fprintf(os.Stdout, "*") - os.Stdout.Sync() + fmt.Fprintf(out, "*") if count >= hookBatchSize { - fmt.Fprintf(os.Stdout, " Checking %d branches\n", count) - os.Stdout.Sync() + fmt.Fprintf(out, " Checking %d branches\n", count) hookOptions.OldCommitIDs = oldCommitIDs hookOptions.NewCommitIDs = newCommitIDs @@ -147,12 +238,10 @@ Gitea or set your environment appropriately.`, "") lastline = 0 } } else { - fmt.Fprintf(os.Stdout, ".") - os.Stdout.Sync() + fmt.Fprintf(out, ".") } if lastline >= hookBatchSize { - fmt.Fprintf(os.Stdout, "\n") - os.Stdout.Sync() + fmt.Fprintf(out, "\n") lastline = 0 } } @@ -162,8 +251,7 @@ Gitea or set your environment appropriately.`, "") hookOptions.NewCommitIDs = newCommitIDs[:count] hookOptions.RefFullNames = refFullNames[:count] - fmt.Fprintf(os.Stdout, " Checking %d branches\n", count) - os.Stdout.Sync() + fmt.Fprintf(out, " Checking %d branches\n", count) statusCode, msg := private.HookPreReceive(username, reponame, hookOptions) switch statusCode { @@ -173,14 +261,11 @@ Gitea or set your environment appropriately.`, "") fail(msg, "") } } else if lastline > 0 { - fmt.Fprintf(os.Stdout, "\n") - os.Stdout.Sync() + fmt.Fprintf(out, "\n") lastline = 0 } - fmt.Fprintf(os.Stdout, "Checked %d references in total\n", total) - os.Stdout.Sync() - + fmt.Fprintf(out, "Checked %d references in total\n", total) return nil } @@ -206,6 +291,19 @@ Gitea or set your environment appropriately.`, "") } } + var out io.Writer + var dWriter *delayWriter + out = &nilWriter{} + if setting.Git.VerbosePush { + if setting.Git.VerbosePushDelay > 0 { + dWriter = newDelayWriter(os.Stdout, setting.Git.VerbosePushDelay) + defer dWriter.Close() + out = dWriter + } else { + out = os.Stdout + } + } + // the environment setted on serv command repoUser := os.Getenv(models.EnvRepoUsername) isWiki := (os.Getenv(models.EnvRepoIsWiki) == "true") @@ -241,7 +339,7 @@ Gitea or set your environment appropriately.`, "") continue } - fmt.Fprintf(os.Stdout, ".") + fmt.Fprintf(out, ".") oldCommitIDs[count] = string(fields[0]) newCommitIDs[count] = string(fields[1]) refFullNames[count] = string(fields[2]) @@ -250,16 +348,15 @@ Gitea or set your environment appropriately.`, "") } count++ total++ - os.Stdout.Sync() if count >= hookBatchSize { - fmt.Fprintf(os.Stdout, " Processing %d references\n", count) - os.Stdout.Sync() + fmt.Fprintf(out, " Processing %d references\n", count) hookOptions.OldCommitIDs = oldCommitIDs hookOptions.NewCommitIDs = newCommitIDs hookOptions.RefFullNames = refFullNames resp, err := private.HookPostReceive(repoUser, repoName, hookOptions) if resp == nil { + _ = dWriter.Close() hookPrintResults(results) fail("Internal Server Error", err) } @@ -277,9 +374,9 @@ Gitea or set your environment appropriately.`, "") fail("Internal Server Error", "SetDefaultBranch failed with Error: %v", err) } } - fmt.Fprintf(os.Stdout, "Processed %d references in total\n", total) - os.Stdout.Sync() + fmt.Fprintf(out, "Processed %d references in total\n", total) + _ = dWriter.Close() hookPrintResults(results) return nil } @@ -288,19 +385,18 @@ Gitea or set your environment appropriately.`, "") hookOptions.NewCommitIDs = newCommitIDs[:count] hookOptions.RefFullNames = refFullNames[:count] - fmt.Fprintf(os.Stdout, " Processing %d references\n", count) - os.Stdout.Sync() + fmt.Fprintf(out, " Processing %d references\n", count) resp, err := private.HookPostReceive(repoUser, repoName, hookOptions) if resp == nil { + _ = dWriter.Close() hookPrintResults(results) fail("Internal Server Error", err) } wasEmpty = wasEmpty || resp.RepoWasEmpty results = append(results, resp.Results...) - fmt.Fprintf(os.Stdout, "Processed %d references in total\n", total) - os.Stdout.Sync() + fmt.Fprintf(out, "Processed %d references in total\n", total) if wasEmpty && masterPushed { // We need to tell the repo to reset the default branch to master @@ -309,7 +405,7 @@ Gitea or set your environment appropriately.`, "") fail("Internal Server Error", "SetDefaultBranch failed with Error: %v", err) } } - + _ = dWriter.Close() hookPrintResults(results) return nil diff --git a/docs/content/doc/advanced/config-cheat-sheet.en-us.md b/docs/content/doc/advanced/config-cheat-sheet.en-us.md index dc6a1ba346..ea17096ea5 100644 --- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md +++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md @@ -522,6 +522,8 @@ NB: You must `REDIRECT_MACARON_LOG` and have `DISABLE_ROUTER_LOG` set to `false` - `MAX_GIT_DIFF_FILES`: **100**: Max number of files shown in diff view. - `GC_ARGS`: **\**: Arguments for command `git gc`, e.g. `--aggressive --auto`. See more on http://git-scm.com/docs/git-gc/ - `ENABLE_AUTO_GIT_WIRE_PROTOCOL`: **true**: If use git wire protocol version 2 when git version >= 2.18, default is true, set to false when you always want git wire protocol version 1 +- `VERBOSE_PUSH`: **true**: Print status information about pushes as they are being processed. +- `VERBOSE_PUSH_DELAY`: **5s**: Only print verbose information if push takes longer than this delay. ## Git - Timeout settings (`git.timeout`) - `DEFAUlT`: **360**: Git operations default timeout seconds. diff --git a/modules/setting/git.go b/modules/setting/git.go index 8495be8836..8c8179cba6 100644 --- a/modules/setting/git.go +++ b/modules/setting/git.go @@ -21,6 +21,8 @@ var ( MaxGitDiffLines int MaxGitDiffLineCharacters int MaxGitDiffFiles int + VerbosePush bool + VerbosePushDelay time.Duration GCArgs []string `ini:"GC_ARGS" delim:" "` EnableAutoGitWireProtocol bool Timeout struct { @@ -36,6 +38,8 @@ var ( MaxGitDiffLines: 1000, MaxGitDiffLineCharacters: 5000, MaxGitDiffFiles: 100, + VerbosePush: true, + VerbosePushDelay: 5 * time.Second, GCArgs: []string{}, EnableAutoGitWireProtocol: true, Timeout: struct {