From a4967cf53207c5a11a9bd4031b22e2bfe1e3a479 Mon Sep 17 00:00:00 2001
From: Alexander Medvednikov
Date: Sat, 16 May 2026 23:11:21 +0300
Subject: [PATCH] clone: stream git clone progress to repo's progress file
---
git/git.v | 52 +++++++++++++++++++++++++++++++
static/css/gitly.scss | 16 ++++++++++
templates/cloning_in_process.html | 6 +++-
3 files changed, 73 insertions(+), 1 deletion(-)
diff --git a/git/git.v b/git/git.v
index 99096a6..b54b6b5 100644
--- a/git/git.v
+++ b/git/git.v
@@ -1,6 +1,7 @@
module git
import os
+import time
pub struct Git {}
@@ -29,6 +30,57 @@ pub fn Git.clone(url string, path string) os.Result {
return os.exec(['git', 'clone', '--bare', url, path])
}
+// Git.clone_with_progress runs `git clone --bare --progress` and streams
+// every byte of git's progress output (which goes to stderr) into
+// `progress_path` while the clone is running, so a separate process can
+// poll the file and show live progress to the user.
+pub fn Git.clone_with_progress(url string, path string, progress_path string) os.Result {
+ println('new clone (progress) url="${url}" path="${path}" progress="${progress_path}"')
+ os.rm(progress_path) or {}
+ mut p := os.new_process('git')
+ p.set_args(['clone', '--bare', '--progress', url, path])
+ p.set_redirect_stdio()
+ p.run()
+ mut log := os.open_append(progress_path) or {
+ eprintln('clone_with_progress: cannot open progress file "${progress_path}": ${err}')
+ // fall back to non-streaming behaviour
+ p.wait()
+ out := p.stdout_slurp() + p.stderr_slurp()
+ code := p.code
+ p.close()
+ return os.Result{
+ exit_code: code
+ output: out
+ }
+ }
+ mut collected := ''
+ for p.is_alive() {
+ chunk := p.stderr_read()
+ if chunk.len > 0 {
+ log.write_string(chunk) or {}
+ log.flush()
+ collected += chunk
+ }
+ // drain stdout so the pipe buffer never blocks the child
+ _ := p.stdout_read()
+ time.sleep(100 * time.millisecond)
+ }
+ final := p.stderr_slurp()
+ if final.len > 0 {
+ log.write_string(final) or {}
+ log.flush()
+ collected += final
+ }
+ log.close()
+ p.wait()
+ exit_code := p.code
+ p.close()
+ return os.Result{
+ exit_code: exit_code
+ output: collected
+ }
+}
+
pub fn Git.show_file_blob(repo_dir string, branch string, file_path string) !string {
result := Git.exec_in_dir(repo_dir, ['--no-pager', 'show', '${branch}:${file_path}'])
if result.exit_code != 0 {
diff --git a/static/css/gitly.scss b/static/css/gitly.scss
index 1aedd46..b7e81f0 100644
--- a/static/css/gitly.scss
+++ b/static/css/gitly.scss
@@ -101,6 +101,22 @@ pre > code {
}
}
+.clone-status-log {
+ margin: 20px auto 0;
+ max-width: 720px;
+ padding: 12px 16px;
+ background: #f6f8fa;
+ border: 1px solid #e1e4e8;
+ border-radius: $small-radius;
+ color: $gray-dark;
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
+ font-size: 12px;
+ line-height: 1.5;
+ text-align: left;
+ white-space: pre-wrap;
+ word-break: break-word;
+}
+
#top_right_div {
float:right;
display: flex;
diff --git a/templates/cloning_in_process.html b/templates/cloning_in_process.html
index 5b587e5..dba39c6 100644
--- a/templates/cloning_in_process.html
+++ b/templates/cloning_in_process.html
@@ -15,7 +15,11 @@
%repo_being_cloned_clone_url @clone_url
@end
-
+ @if clone_progress != ''
+ @clone_progress
+ @else
+
+ @end
--
2.39.5