From 1fbeec1ebbd6a39fe565a31e861235ae2a78dd52 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Tue, 12 May 2026 11:39:19 +0300 Subject: [PATCH] remove libgit references entirely --- ci/ci_routes.v | 21 +-- ci/ci_trigger.v | 3 +- git/git.v | 71 ++++++++++- git/libgit.v | 291 ------------------------------------------ git/util.v | 15 +-- git/utils_test.v | 11 ++ gitly.v | 3 +- repo/file_routes.v | 15 ++- repo/repo.v | 15 +-- repo/repo_routes.v | 8 +- static/assets/version | 2 +- 11 files changed, 110 insertions(+), 345 deletions(-) delete mode 100644 git/libgit.v diff --git a/ci/ci_routes.v b/ci/ci_routes.v index 7d5f1a8..3d995bc 100644 --- a/ci/ci_routes.v +++ b/ci/ci_routes.v @@ -4,8 +4,8 @@ import veb import api import json import net.http -import os import time +import git struct CiStatusCallback { run_id string @@ -49,7 +49,8 @@ pub fn (mut app App) ci_runs(username string, repo_name string) veb.Result { } // Check if .gitly-ci.yml exists in the repo - has_ci_file := os.execute('git -C ${repo.git_dir} show ${repo.primary_branch}:.gitly-ci.yml').exit_code == 0 + has_ci_file := git.Git.exec_in_dir(repo.git_dir, + ['show', '${repo.primary_branch}:.gitly-ci.yml']).exit_code == 0 // Fetch runs from gitly_ci service for a complete list mut ci_runs := []CiRunListItem{} @@ -103,9 +104,7 @@ pub fn (mut app App) ci_run_detail(username string, repo_name string, run_id_str } ci_url := '${app.config.ci_service_url}/api/v1/runs/${ci_run_id}' - response := http.get(ci_url) or { - return ctx.not_found() - } + response := http.get(ci_url) or { return ctx.not_found() } if response.status_code != 200 { return ctx.not_found() @@ -114,9 +113,7 @@ pub fn (mut app App) ci_run_detail(username string, repo_name string, run_id_str ci_run_json := response.body // Parse the response to display - run_data := json.decode(CiApiRunResponse, ci_run_json) or { - return ctx.not_found() - } + run_data := json.decode(CiApiRunResponse, ci_run_json) or { return ctx.not_found() } ci_run := run_data.result @@ -141,17 +138,13 @@ pub fn (mut app App) ci_restart_run(username string, repo_name string, run_id_st // Call gitly_ci restart API restart_url := '${app.config.ci_service_url}/api/v1/runs/${ci_run_id}/restart' - response := http.post(restart_url, '') or { - return ctx.not_found() - } + response := http.post(restart_url, '') or { return ctx.not_found() } if response.status_code != 200 { return ctx.not_found() } - result := json.decode(CiApiRunResponse, response.body) or { - return ctx.not_found() - } + result := json.decode(CiApiRunResponse, response.body) or { return ctx.not_found() } if result.success { new_run := result.result diff --git a/ci/ci_trigger.v b/ci/ci_trigger.v index 0733282..a3f7fa8 100644 --- a/ci/ci_trigger.v +++ b/ci/ci_trigger.v @@ -3,6 +3,7 @@ module main import json import net.http import os +import git struct CiTriggerPayload { repo_id int @@ -32,7 +33,7 @@ fn (mut app App) trigger_ci_if_configured(repo_id int, branch_name string) { } // Read .gitly-ci.yml from the repo using git show (works with bare repos) - show_result := os.execute('git -C ${repo.git_dir} show ${branch_name}:.gitly-ci.yml') + show_result := git.Git.exec_in_dir(repo.git_dir, ['show', '${branch_name}:.gitly-ci.yml']) if show_result.exit_code != 0 || show_result.output.trim_space() == '' { app.info('No .gitly-ci.yml found in ${repo.name}/${branch_name}') return diff --git a/git/git.v b/git/git.v index 07f287f..99096a6 100644 --- a/git/git.v +++ b/git/git.v @@ -4,8 +4,24 @@ import os pub struct Git {} -pub fn clone(url string, path string) os.Result { - return Git.clone(url, path) +pub fn Git.exec(args []string) os.Result { + mut git_args := ['git'] + git_args << args + return os.exec(git_args) +} + +pub fn Git.exec_in_dir(dir string, args []string) os.Result { + mut git_args := ['-C', dir] + git_args << args + return Git.exec(git_args) +} + +pub fn Git.exec_in_dir_command(dir string, command string) os.Result { + return Git.exec_in_dir(dir, split_command(command)) +} + +pub fn Git.exec_shell(command string) os.Result { + return os.exec(['/bin/sh', '-c', command]) } pub fn Git.clone(url string, path string) os.Result { @@ -13,6 +29,53 @@ pub fn Git.clone(url string, path string) os.Result { return os.exec(['git', 'clone', '--bare', url, path]) } -pub fn (mut r Repo) clone(url string, path string) os.Result { - return Git.clone(url, path) +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 { + return error(result.output) + } + return result.output +} + +fn split_command(command string) []string { + mut args := []string{} + mut current := []u8{} + mut quote := u8(0) + mut escaped := false + + for ch in command.bytes() { + if escaped { + current << ch + escaped = false + continue + } + if ch == `\\` { + escaped = true + continue + } + if quote != 0 { + if ch == quote { + quote = 0 + } else { + current << ch + } + continue + } + if ch == `"` || ch == `'` { + quote = ch + continue + } + if ch.is_space() { + if current.len > 0 { + args << current.bytestr() + current.clear() + } + continue + } + current << ch + } + if current.len > 0 { + args << current.bytestr() + } + return args } diff --git a/git/libgit.v b/git/libgit.v deleted file mode 100644 index c6144c2..0000000 --- a/git/libgit.v +++ /dev/null @@ -1,291 +0,0 @@ -module git - -// import time - -//#include "stdint.h" - -#flag darwin -I/opt/homebrew/include -#flag darwin -L/opt/homebrew/lib - -#flag linux -I/usr/local/include -#flag linux -L/usr/local/lib - -#flag -lgit2 - -// #flag windows -Igit/ - -#include "git2/types.h" -#include "git2/common.h" -#include "git2/global.h" -#include "git2/repository.h" - -#include "git2.h" - -fn C.git_libgit2_init() -fn C.git_libgit2_shutdown() -fn C.git_repository_init(repo voidptr, path &char, is_bare bool) int -fn C.git_repository_open(repo voidptr, path &char) int - -fn C.git_libgit2_features() -fn C.git_commit_lookup(voidptr, voidptr, &C.git_oid) int - -fn C.git_repository_free(repo &C.git_repository) -fn C.git_repository_head(out &&C.git_reference, repo &C.git_repository) int - -fn C.git_reference_free(ref &C.git_reference) - -struct C.git_repository {} - -struct C.git_commit {} - -struct C.git_signature { - name &char - email &char -} - -struct C.git_oid {} - -struct C.git_reference {} - -struct C.git_revwalk {} - -struct C.git_object {} - -struct C.git_error { - message &char -} - -@[typedef] -struct C.git_tree_entry {} - -fn C.git_commit_message(voidptr) &char -fn C.git_reference_lookup(&&C.git_reference, &C.git_repository, &char) -fn C.git_reference_symbolic_target(&C.git_reference) &char -fn C.git_revwalk_next(&C.git_oid, &C.git_revwalk) int -fn C.git_revwalk_new(&&C.git_revwalk, &C.git_repository) int -fn C.git_revwalk_push(&C.git_revwalk, &C.git_oid) int -fn C.git_reference_name_to_id(&C.git_oid, &C.git_repository, &char) -fn C.git_oid_tostr_s(&C.git_oid) &char -fn C.git_commit_author(&C.git_commit) &C.git_signature -fn C.git_branch_lookup(&&C.git_reference, &C.git_repository, &char, int) int -fn C.git_error_last() &C.git_error -fn C.git_reference_peel(&&C.git_object, &C.git_reference, int) int - -// fn C.git_commit_tree(&&C.git_tree, &C.git_commit) -fn C.git_commit_tree(voidptr, &C.git_commit) -fn C.git_tree_entrycount(&C.git_tree) int -fn C.git_tree_entry_byindex(&C.git_tree, int) &C.git_tree_entry -fn C.git_tree_entry_name(&C.git_tree_entry) &char -fn C.git_blob_lookup(&&C.git_blob, &C.git_repository, &C.git_oid) int -fn C.git_tree_entry_id(&C.git_tree_entry) &C.git_oid -fn C.git_blob_rawcontent(&C.git_blob) voidptr -fn C.git_blob_rawsize(&C.git_blob) int -fn C.git_blob_free(&C.git_blob) -fn C.git_object_lookup_bypath(&&C.git_object, &C.git_object, &char, int) int -fn C.git_object_peel(&&C.git_object, &C.git_object, int) int -fn C.git_object_id(&C.git_object) &C.git_oid - -fn init() { - C.git_libgit2_init() -} - -fn shutdown() { - C.git_libgit2_shutdown() -} - -pub struct Repo { - obj &C.git_repository = unsafe { nil } - - path string -} - -fn (r Repo) str() string { - return 'Repo{ path:${r.path} }' -} - -@[params] -struct LogParams { - n int - dir string // -C "dir" - branch string -} - -struct Commit { -mut: - msg string - hash string - author_name string - author_email string -} - -fn (r Repo) log(p LogParams) []Commit { - oid := C.git_oid{} - C.git_reference_name_to_id(&oid, r.obj, c'HEAD') - commit := &C.git_commit(unsafe { nil }) - walker := &C.git_revwalk(unsafe { nil }) - if C.git_revwalk_new(&walker, r.obj) != 0 { - println('failed to create walker') - return [] - } - C.git_revwalk_push(walker, &oid) - mut commits := []Commit{cap: 10} - mut i := 0 - mut cur_oid := C.git_oid{} - for C.git_revwalk_next(&cur_oid, walker) == 0 { - mut gitly_commit := Commit{} - // println('RET =${ret0}') - // println(C.GIT_ITEROVER) - C.git_commit_lookup(&commit, r.obj, &cur_oid) - // Get commit message - msg := C.git_commit_message(commit) - // Get commit id (hash) - commit_id := C.git_oid_tostr_s(&cur_oid) - // Get commit author - author := C.git_commit_author(commit) - unsafe { - if msg == nil || commit_id == nil || author == nil || author.name == nil { - println('nil log, skipping') - continue - } - } - gitly_commit.msg = unsafe { cstring_to_vstring(msg) } - gitly_commit.hash = unsafe { cstring_to_vstring(commit_id) } - gitly_commit.author_name = unsafe { cstring_to_vstring(author.name) } - gitly_commit.author_email = unsafe { cstring_to_vstring(author.email) } - println(gitly_commit) - commits << gitly_commit - - i++ - if p.n > 0 && i >= p.n { - // Reached the limit `n` (like `git log -n10`) - break - } - } - return commits -} - -pub fn new_repo(path string) &Repo { - repo_obj := &C.git_repository(unsafe { nil }) - // ret := C.git_repository_init(&repo_obj, path.str, false) - ret := C.git_repository_open(&repo_obj, path.str) - println('ff ${ret}') - // git_reference_free(head_ref); - return &Repo{ - obj: repo_obj - path: path - } -} - -pub fn (r &Repo) current_branch() string { - head_ref := &C.git_reference(unsafe { nil }) - C.git_reference_lookup(&head_ref, r.obj, c'HEAD') - symbolic_ref := C.git_reference_symbolic_target(head_ref) - branch := unsafe { cstring_to_vstring(symbolic_ref) } - return branch.after('refs/heads/') -} - -pub fn (repo &Repo) primary_branch() string { - err := C.git_repository_open(&repo.obj, repo.path.str) - if err != 0 { - return '' - } - defer { - C.git_repository_free(repo.obj) - } - - // Get HEAD reference - head_ref := &C.git_reference(unsafe { nil }) - err_head := C.git_repository_head(&head_ref, repo.obj) - if err_head != 0 { - return '' - } - defer { - C.git_reference_free(head_ref) - } - - // Get symbolic target - symbolic_ref := C.git_reference_symbolic_target(head_ref) - if symbolic_ref == unsafe { nil } { - return '' - } - - // Convert to V string and extract branch name - branch := unsafe { cstring_to_vstring(symbolic_ref) } - return get_branch_name_from_reference(branch) -} - -fn get_branch_name_from_reference(ref string) string { - return ref.after('refs/heads/') -} - -pub fn (r &Repo) show_file_blob(branch string, file_path string) !string { - mut blob := &C.git_blob(unsafe { nil }) - mut branch_ref := &C.git_reference(unsafe { nil }) - if C.git_branch_lookup(&branch_ref, r.obj, branch.str, C.GIT_BRANCH_LOCAL) != 0 { - C.printf(c'Failed to lookup branch: %s\n', C.git_error_last().message) - return error('sdf') - } - - mut treeish := &C.git_object(unsafe { nil }) - if C.git_reference_peel(&treeish, branch_ref, C.GIT_OBJECT_COMMIT) != 0 { - C.printf(c'Failed to peel reference to commit: %s\n', C.git_error_last().message) - return error('sdf') - } - - commit := &C.git_commit(treeish) - C.git_commit_tree(&treeish, commit) - if commit == unsafe { nil } { - C.printf(c'Failed to get commit tree\n') - return error('sdf') - } - // create file object - mut file_obj := &C.git_object(unsafe { nil }) - - // get file object - if C.git_object_lookup_bypath(&file_obj, treeish, file_path.str, C.GIT_OBJECT_BLOB) != 0 { - C.printf(c'Failed to lookup file: %s\n', C.git_error_last().message) - return error('sdf') - } - - // get file oid - mut oid := C.git_object_id(file_obj) - - // get blob object - if C.git_blob_lookup(&blob, r.obj, oid) != 0 { - C.printf(c'Failed to lookup blob: %s\n', C.git_error_last().message) - return error('sdf') - } - - // get blob content - content := C.git_blob_rawcontent(blob) - // size := C.git_blob_rawsize(blob) - - C.printf(c'Content of %s (from branch %s):\n', file_path.str, branch.str) - - text := unsafe { cstring_to_vstring(content) } - C.git_blob_free(blob) - return text -} - -/* -fn main() { - t0 := time.now() - r := new_repo('/Users/alex/code/gitly/repos/admin/vlang2') - /* - println(time.since(t0)) - println('1current branch=') - b := r.current_branch() - println(b) - t := time.now() - r.log(n: 10) - println(time.since(t)) - */ - println('GIT SHOW') - - t := time.now() - println(r.show_file_blob('master', 'README.md')!) - println(time.since(t)) - C.git_libgit2_features() - println(r) -} -*/ diff --git a/git/util.v b/git/util.v index 26eeeb0..9d198f1 100644 --- a/git/util.v +++ b/git/util.v @@ -63,24 +63,13 @@ pub fn check_git_repo_url(url string) bool { } pub fn get_git_executable_path() ?string { - which_result := os.execute('which git') - which_exit_code := which_result.exit_code - which_output := which_result.output - if which_exit_code != 0 { - return none - } - return which_output.trim(' \n') + return os.find_abs_path_of_executable('git') or { none } } pub fn remove_git_extension_if_exists(git_repository_name string) string { return git_repository_name.trim_string_right('.git') } -/* fn get_branch_name_from_reference(value string) string { - branch_query := r'refs/heads/(.*)' - mut re := regex.regex_opt(branch_query) or { panic(err) } - re.match_string(value) - return re.get_group_by_id(value, 0) + return value.after('refs/heads/') } -*/ diff --git a/git/utils_test.v b/git/utils_test.v index d59d37c..0353eba 100644 --- a/git/utils_test.v +++ b/git/utils_test.v @@ -5,3 +5,14 @@ fn test_get_branch_name_from_reference() { assert get_branch_name_from_reference('refs/heads/main') == 'main' assert get_branch_name_from_reference('refs/heads/fix-110') == 'fix-110' } + +fn test_split_command_keeps_quoted_arguments() { + assert split_command('--no-pager log main --pretty="%h %s"') == ['--no-pager', 'log', 'main', + '--pretty=%h %s'] + assert split_command('archive v1.0 --format=zip --output="/tmp/release archive.zip"') == [ + 'archive', + 'v1.0', + '--format=zip', + '--output=/tmp/release archive.zip', + ] +} diff --git a/gitly.v b/gitly.v index 8889d6b..944b8a5 100644 --- a/gitly.v +++ b/gitly.v @@ -8,6 +8,7 @@ import os import log import api import config +import git const commits_per_page = 35 const expire_length = 200 @@ -77,7 +78,7 @@ fn new_app() !&App { stored_version := os.read_file(version_path) or { 'unknown' } mut version := stored_version - git_result := os.execute('git rev-parse --short HEAD') + git_result := git.Git.exec(['rev-parse', '--short', 'HEAD']) if git_result.exit_code == 0 && !git_result.output.contains('fatal') { version = git_result.output.trim_space() diff --git a/repo/file_routes.v b/repo/file_routes.v index 1d54342..d4cff55 100644 --- a/repo/file_routes.v +++ b/repo/file_routes.v @@ -2,6 +2,7 @@ module main import veb import os +import git // GET /:username/:repo_name/new/:branch_name - Show create file form @['/:username/:repo_name/new/:branch_name'] @@ -219,7 +220,7 @@ fn (mut app App) create_file_in_bare_repo(mut repo Repo, branch string, file_pat // Read existing tree into temp index r1 := - os.execute('/bin/sh -c \'GIT_INDEX_FILE=${tmp_index} git -C ${git_dir} read-tree ${existing_tree}\'') + git.Git.exec_shell('GIT_INDEX_FILE=${tmp_index} git -C ${git_dir} read-tree ${existing_tree}') if r1.exit_code != 0 { app.warn('read-tree failed: ${r1.output}') return false @@ -227,14 +228,14 @@ fn (mut app App) create_file_in_bare_repo(mut repo Repo, branch string, file_pat // Add the new blob to the index r2 := - os.execute('/bin/sh -c \'GIT_INDEX_FILE=${tmp_index} git -C ${git_dir} update-index --add --cacheinfo 100644,${blob_hash},${file_path}\'') + git.Git.exec_shell('GIT_INDEX_FILE=${tmp_index} git -C ${git_dir} update-index --add --cacheinfo 100644,${blob_hash},${file_path}') if r2.exit_code != 0 { app.warn('update-index failed: ${r2.output}') return false } // Write the tree - r3 := os.execute('/bin/sh -c \'GIT_INDEX_FILE=${tmp_index} git -C ${git_dir} write-tree\'') + r3 := git.Git.exec_shell('GIT_INDEX_FILE=${tmp_index} git -C ${git_dir} write-tree') if r3.exit_code != 0 { app.warn('write-tree failed: ${r3.output}') return false @@ -248,7 +249,7 @@ fn (mut app App) create_file_in_bare_repo(mut repo Repo, branch string, file_pat defer { os.rm(tmp_tree) or {} } - r := os.execute('/bin/sh -c \'git -C ${git_dir} mktree < ${tmp_tree}\'') + r := git.Git.exec_shell('git -C ${git_dir} mktree < ${tmp_tree}') if r.exit_code != 0 { app.warn('mktree failed: ${r.output}') return false @@ -268,7 +269,7 @@ fn (mut app App) create_file_in_bare_repo(mut repo Repo, branch string, file_pat } commit_sh := 'GIT_AUTHOR_NAME="${author}" GIT_AUTHOR_EMAIL="${author}@gitly" GIT_COMMITTER_NAME="${author}" GIT_COMMITTER_EMAIL="${author}@gitly" git -C ${git_dir} commit-tree ${new_tree_hash} ${parent_flag} -m "${message}"' - r4 := os.execute("/bin/sh -c '${commit_sh}'") + r4 := git.Git.exec_shell(commit_sh) if r4.exit_code != 0 { app.warn('commit-tree failed: ${r4.output}') return false @@ -276,7 +277,7 @@ fn (mut app App) create_file_in_bare_repo(mut repo Repo, branch string, file_pat new_commit_hash := r4.output.trim_space() // 5. Update the branch ref - r5 := os.execute('git -C ${git_dir} update-ref refs/heads/${branch} ${new_commit_hash}') + r5 := git.Git.exec_in_dir(git_dir, ['update-ref', 'refs/heads/${branch}', new_commit_hash]) if r5.exit_code != 0 { app.warn('update-ref failed: ${r5.output}') return false @@ -287,7 +288,7 @@ fn (mut app App) create_file_in_bare_repo(mut repo Repo, branch string, file_pat } fn sh(cmd string) string { - r := os.execute('/bin/sh -c \'${cmd}\'') + r := git.Git.exec_shell(cmd) if r.exit_code != 0 { return '' } diff --git a/repo/repo.v b/repo/repo.v index cc7d422..4987c32 100644 --- a/repo/repo.v +++ b/repo/repo.v @@ -25,7 +25,6 @@ struct Repo { latest_update_hash string @[skip] latest_activity time.Time @[skip] mut: - git_repo &git.Repo = unsafe { nil } @[skip] // libgit wrapper repo webhook_secret string tags_count int nr_open_issues int @[orm: 'open_issues_count'] @@ -103,7 +102,6 @@ fn (app App) find_repo_by_name_and_user_id(repo_name string, user_id int) ?Repo mut repo := repos[0] repo.lang_stats = app.find_repo_lang_stats(repo.id) println('GIT DIR = ${repo.git_dir}') - repo.git_repo = git.new_repo(repo.git_dir) return repo } @@ -146,7 +144,6 @@ fn (app &App) search_public_repos(query string) []Repo { repos << Repo{ id: row[0].int() - git_repo: unsafe { nil } name: row[1] user_name: user.username description: row[3] @@ -574,7 +571,7 @@ fn (r &Repo) git(command string) string { command_with_path := '-C ${r.git_dir} ${command}' - command_result := os.execute('git ${command_with_path}') + command_result := git.Git.exec_in_dir_command(r.git_dir, command) command_exit_code := command_result.exit_code if command_exit_code != 0 { println('git error ${command_with_path} with ${command_exit_code} exit code out=${command_result.output}') @@ -695,7 +692,8 @@ fn (mut app App) slow_fetch_files_info(mut repo Repo, branch string, path string } fn (r Repo) get_last_branch_commit_hash(branch_name string) string { - git_result := os.execute('git -C ${r.git_dir} log -n 1 ${branch_name} --pretty=format:"%h"') + git_result := git.Git.exec_in_dir(r.git_dir, + ['log', '-n', '1', branch_name, '--pretty=format:%h']) git_output := git_result.output if git_result.exit_code != 0 { @@ -706,7 +704,7 @@ fn (r Repo) get_last_branch_commit_hash(branch_name string) string { } fn (r Repo) git_advertise(service string) string { - git_result := os.execute('git ${service} --stateless-rpc --advertise-refs ${r.git_dir}') + git_result := git.Git.exec([service, '--stateless-rpc', '--advertise-refs', r.git_dir]) git_output := git_result.output if git_result.exit_code != 0 { @@ -813,13 +811,10 @@ fn (r &Repo) read_file(branch string, path string) string { valid_path := path.trim_string_left('/') println('read_file() path=${valid_path}') - if r.git_repo == unsafe { nil } { - return 'nil' - } t := time.now() // s := r.git('--no-pager show ${branch}:${valid_path}') - s := r.git_repo.show_file_blob(branch, valid_path) or { '' } + s := git.Git.show_file_blob(r.git_dir, branch, valid_path) or { '' } println(time.since(t)) println(':)') return s diff --git a/repo/repo_routes.v b/repo/repo_routes.v index ba753de..4f66622 100644 --- a/repo/repo_routes.v +++ b/repo/repo_routes.v @@ -237,7 +237,6 @@ pub fn (mut app App) handle_new_repo(mut ctx Context, name string, clone_url str repo_path := os.join_path(app.config.repo_storage_path, ctx.user.username, name) id := app.get_count_repo() + 1 mut new_repo := &Repo{ - git_repo: git.new_repo(repo_path) name: name id: id description: description @@ -346,9 +345,10 @@ pub fn (mut app App) tree(mut ctx Context, username string, repo_name string, br eprintln('tree() repo ${repo_name} not found') return ctx.not_found() } + mut clone_url := '' eprintln('!!! REPO STATUS = ${repo.status}') if repo.status == .cloning { - clone_url := app.clone_urls[repo.id] or { '' } + clone_url = app.clone_urls[repo.id] or { '' } return $veb.html('templates/cloning_in_process.html') } app.clone_urls.delete(repo.id) @@ -416,6 +416,7 @@ pub fn (mut app App) tree(mut ctx Context, username string, repo_name string, br // Fetch last commit message for this directory, printed at the top of the tree mut last_commit := Commit{} + mut dir := File{} if can_up { mut p := path if p.ends_with('/') { @@ -424,7 +425,8 @@ pub fn (mut app App) tree(mut ctx Context, username string, repo_name string, br if !p.contains('/') { p = '/${p}' } - if dir := app.find_repo_file_by_path(repo.id, branch_name, p) { + dir = app.find_repo_file_by_path(repo.id, branch_name, p) or { File{} } + if dir.id != 0 { last_commit = app.find_repo_commit_by_hash(repo.id, dir.last_hash) } } else { diff --git a/static/assets/version b/static/assets/version index aa7fddc..2a4f6ba 100644 --- a/static/assets/version +++ b/static/assets/version @@ -1 +1 @@ -5d8107c \ No newline at end of file +17d5bf8 \ No newline at end of file -- 2.39.5