This commit is contained in:
2025-08-15 06:30:12 +02:00
parent bd86f2c4f7
commit e77f923cd2
6 changed files with 247 additions and 263 deletions

View File

@@ -236,50 +236,39 @@ repo.remove_changes()!
repo.update_submodules()!
```
## Repository Configuration
## Repository Configuration & Status
### GitRepo Structure
The `gittools` module uses an imperative model. The `GitRepo` struct holds the *current* status of a repository in a unified `GitStatus` object. To change the state, you call explicit functions like `repo.branch_switch('my-feature')`.
### GitRepo and GitStatus Structure
```v
// GitRepo represents a single git repository.
pub struct GitRepo {
pub mut:
provider string // e.g., github.com
account string // Git account name
name string // Repository name
status_remote GitRepoStatusRemote // Remote repository status
status_local GitRepoStatusLocal // Local repository status
status_wanted GitRepoStatusWanted // Desired status
config GitRepoConfig // Repository configuration
deploysshkey string // SSH key for git operations
}
```
### Status Tracking
```v
// Remote Status
pub struct GitRepoStatusRemote {
pub mut:
ref_default string // Default branch hash
branches map[string]string // Branch name -> commit hash
tags map[string]string // Tag name -> commit hash
provider string
account string
name string
config GitRepoConfig
status GitStatus // Unified struct holding the CURRENT repo status.
}
// Local Status
pub struct GitRepoStatusLocal {
// GitStatus holds all live status information for a repository.
pub struct GitStatus {
pub mut:
branches map[string]string // Branch name -> commit hash
branch string // Current branch
tag string // Current tag
}
// Desired Status
pub struct GitRepoStatusWanted {
pub mut:
branch string
tag string
url string // Remote repository URL
readonly bool // Prevent push/commit operations
// State from local and remote (`git fetch`)
branches map[string]string // branch name -> commit hash
tags map[string]string // tag name -> commit hash
// Current local state
branch string // The current checked-out branch
tag string // The current checked-out tag (if any)
ahead int // Commits ahead of remote
behind int // Commits behind remote
// Overall status
has_changes bool // True if there are uncommitted local changes
error string // Error message if any status update fails
}
```

View File

@@ -85,23 +85,37 @@ site:key → config JSON
site:key:repos:<provider:acct:name> → GitRepo JSON
```
### 4.2 GitRepo (excerpt)
### 4.2 GitRepo & GitStatus
The state of a repository is captured in a single, unified `GitStatus` struct. This struct represents the *current* state of the repository after a `status_update()` operation and does not contain any "desired" or "wanted" state. State changes are performed through imperative function calls (e.g., `repo.branch_switch('main')`).
```v
pub struct GitRepo {
provider string // e.g. github
account string // org/user
name string // repo name
status_remote GitRepoStatusRemote
status_local GitRepoStatusLocal
status_wanted GitRepoStatusWanted
last_load int // epoch
has_changes bool
provider string
account string
name string
config GitRepoConfig
status GitStatus // Unified CURRENT status object
}
pub struct GitStatus {
pub mut:
// Combined local & remote state from `git fetch`
branches map[string]string // branch name -> commit hash
tags map[string]string // tag name -> commit hash
// Current local state
branch string // current checked-out branch
tag string // current checked-out tag (if any)
ahead int // commits ahead of remote
behind int // commits behind remote
// Overall status
has_changes bool // true if uncommitted changes exist
error string // holds error messages from status updates
}
```
Status structs separate **remote**, **local** and **desired** state, enabling `need_*` predicates to remain trivial.
---
## 5. Execution & Behavioural Notes

View File

@@ -1,254 +1,260 @@
// lib/develop/gittools/repository.v
module gittools
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.osal.core as osal
import os
import time
import freeflowuniverse.herolib.core.pathlib
import freeflowuniverse.herolib.develop.vscode
import freeflowuniverse.herolib.develop.sourcetree
import freeflowuniverse.herolib.osal.sshagent
// Commit the staged changes with the provided commit message.
// GitStatus holds the unified status information for a repository.
// It reflects the CURRENT state, not a desired state.
pub struct GitStatus {
pub mut:
// Combined local & remote state (from fetch)
branches map[string]string // All branch names -> commit hash
tags map[string]string // All tag names -> commit hash
// Current local state
branch string // The current checked-out branch.
tag string // The current checked-out tag (if any).
ahead int // Commits ahead of remote.
behind int // Commits behind remote.
// Combined status
has_changes bool // True if there are uncommitted local changes.
error string // Error message if any status update fails.
}
pub struct GitRepoConfig {
pub mut:
remote_check_period int = 300 // seconds, 5 min
}
// GitRepo represents a single git repository.
@[heap]
pub struct GitRepo {
// a git repo is always part of a git structure
mut:
gs &GitStructure
last_load int // epoch when last loaded
pub mut:
provider string // e.g., github.com
account string // Git account name
name string // Repository name
deploysshkey string // SSH key for git operations
config GitRepoConfig
status GitStatus
}
// commit stages all changes and commits them with the provided message.
pub fn (mut repo GitRepo) commit(msg string) ! {
repo.status_update()!
if !repo.need_commit()! {
console.print_debug('No changes to commit.')
console.print_debug('No changes to commit for ${repo.path()}.')
return
}
if msg == '' {
return error('Commit message is empty.')
return error('Commit message cannot be empty.')
}
repo_path := repo.path()
repo.exec('git add . -A') or { return error('Cannot add to repo: ${repo_path}. Error: ${err}') }
repo.exec('git add . -A')!
repo.exec('git commit -m "${msg}"') or {
return error('Cannot commit repo: ${repo_path}. Error: ${err}')
// A common case for failure is when changes are only whitespace changes and git is configured to ignore them.
console.print_debug('Could not commit in ${repo.path()}. Maybe nothing to commit? Error: ${err}')
return
}
console.print_green('Changes committed successfully.')
repo.cache_last_load_clear()! // MODIFIED: Invalidate cache instead of full reload.
console.print_green("Committed changes in '${repo.path()}' with message: '${msg}'.")
repo.cache_last_load_clear()!
}
// Push local changes to the remote repository.
// push local changes to the remote repository.
pub fn (mut repo GitRepo) push() ! {
repo.status_update()!
if repo.need_push_or_pull()! {
url := repo.get_repo_url_for_clone()!
console.print_header('Pushing changes to ${url}')
// We may need to push the locally created branches
repo.exec('git push --set-upstream origin ${repo.status_local.branch}')!
console.print_green('Changes pushed successfully.')
repo.cache_last_load_clear()! // MODIFIED: Invalidate cache instead of full reload.
} else {
console.print_header('Everything is up to date.')
if !repo.need_push()! {
console.print_header('Nothing to push for ${repo.path()}. Already up-to-date.')
return
}
url := repo.get_repo_url_for_clone()!
console.print_header('Pushing changes to ${url}')
// This will push the current branch to its upstream counterpart.
// --set-upstream is useful for new branches.
repo.exec('git push --set-upstream origin ${repo.status.branch}')!
console.print_green('Changes pushed successfully.')
repo.cache_last_load_clear()!
}
@[params]
pub struct PullCheckoutArgs {
pub struct PullArgs {
pub mut:
submodules bool // if we want to pull for submodules
reset bool // if true, will reset local changes before pulling
}
// Pull remote content into the repository.
pub fn (mut repo GitRepo) pull(args_ PullCheckoutArgs) ! {
// pull remote content into the repository.
pub fn (mut repo GitRepo) pull(args PullArgs) ! {
repo.status_update()!
if repo.need_checkout() {
repo.checkout()!
if args.reset {
repo.reset()!
}
repo.exec('git pull') or { return error('Cannot pull repo: ${repo.path()}. Error: ${err}') }
if repo.need_commit()! {
return error('Cannot pull in ${repo.path()} due to uncommitted changes. Either commit them or use the reset:true option.')
}
if args_.submodules {
repo.exec('git pull')!
if args.submodules {
repo.update_submodules()!
}
repo.cache_last_load_clear()! // MODIFIED: Invalidate cache instead of full reload.
console.print_green('Changes pulled successfully.')
}
// Checkout a branch in the repository.
pub fn (mut repo GitRepo) checkout() ! {
repo.status_update()!
if repo.status_wanted.readonly {
repo.reset()!
}
if repo.need_commit()! {
return error('Cannot checkout branch due to uncommitted changes in ${repo.path()}.')
}
if repo.status_wanted.tag.len > 0 {
repo.exec('git checkout tags/${repo.status_wanted.tag}')!
}
if repo.status_wanted.branch.len > 0 {
repo.exec('git checkout ${repo.status_wanted.branch}')!
}
repo.cache_last_load_clear()!
console.print_green('Changes pulled successfully from ${repo.path()}.')
}
// Create a new branch in the repository.
// branch_create creates a new branch.
pub fn (mut repo GitRepo) branch_create(branchname string) ! {
repo.exec('git branch -c ${branchname}') or {
return error('Cannot Create branch: ${repo.path()} to ${branchname}\nError: ${err}')
}
repo.exec('git branch ${branchname}')!
repo.cache_last_load_clear()!
console.print_green('Branch ${branchname} created successfully.')
console.print_green('Branch ${branchname} created successfully in ${repo.path()}.')
}
// branch_switch switches to a different branch.
pub fn (mut repo GitRepo) branch_switch(branchname string) ! {
repo.exec('git switch ${branchname}') or {
return error('Cannot switch branch: ${repo.path()} to ${branchname}\nError: ${err}')
if repo.need_commit()! {
return error('Cannot switch branch in ${repo.path()} due to uncommitted changes.')
}
console.print_green('Branch ${branchname} switched successfully.')
repo.status_local.branch = branchname
repo.status_local.tag = ''
repo.cache_last_load_clear()! // MODIFIED: Invalidate cache instead of full reload.
repo.exec('git switch ${branchname}')!
console.print_green('Switched to branch ${branchname} in ${repo.path()}.')
repo.status.branch = branchname
repo.status.tag = ''
repo.cache_last_load_clear()!
}
// Create a new branch in the repository.
// tag_create creates a new tag.
pub fn (mut repo GitRepo) tag_create(tagname string) ! {
repo_path := repo.path()
repo.exec('git tag ${tagname}') or {
return error('Cannot create tag: ${repo_path}. Error: ${err}')
}
console.print_green('Tag ${tagname} created successfully.')
repo.cache_last_load_clear()! // MODIFIED: Invalidate cache instead of full reload.
repo.exec('git tag ${tagname}')!
console.print_green('Tag ${tagname} created successfully in ${repo.path()}.')
repo.cache_last_load_clear()!
}
// tag_switch checks out a specific tag.
pub fn (mut repo GitRepo) tag_switch(tagname string) ! {
repo.exec('git checkout ${tagname}') or {
return error('Cannot switch to tag: ${tagname}. Error: ${err}')
if repo.need_commit()! {
return error('Cannot switch to tag in ${repo.path()} due to uncommitted changes.')
}
console.print_green('Tag ${tagname} activated.')
repo.status_local.branch = ''
repo.status_local.tag = tagname
repo.cache_last_load_clear()! // MODIFIED: Invalidate cache instead of full reload.
repo.exec('git checkout tags/${tagname}')!
console.print_green('Switched to tag ${tagname} in ${repo.path()}.')
repo.status.branch = ''
repo.status.tag = tagname
repo.cache_last_load_clear()!
}
// Create a new branch in the repository.
// tag_exists checks if a tag exists in the repository.
pub fn (mut repo GitRepo) tag_exists(tag string) !bool {
repo.exec('git show ${tag}') or { return false }
return true
repo.status_update()!
return tag in repo.status.tags
}
// Deletes the Git repository
// delete removes the repository from the filesystem and cache.
pub fn (mut repo GitRepo) delete() ! {
repo_path := repo.path()
key := repo.cache_key()
repo.cache_delete()!
osal.rm(repo_path)!
repo.gs.repos.delete(key) // Remove from GitStructure's repos map
repo.gs.repos.delete(key)
}
// Create GitLocation from the path within the Git repository
pub fn (mut gs GitRepo) gitlocation_from_path(path string) !GitLocation {
// gitlocation_from_path creates a GitLocation from a path inside this repository.
pub fn (mut repo GitRepo) gitlocation_from_path(path string) !GitLocation {
if path.starts_with('/') || path.starts_with('~') {
return error('Path must be relative, cannot start with / or ~')
}
repo.status_update()!
mut git_path := gs.patho()!
mut git_path := repo.patho()!
repo_path := git_path.path
abs_path := os.abs_path(path)
// Check if path is inside git repo
if !abs_path.starts_with(repo_path) {
return error('Path ${path} is not inside the git repository at ${repo_path}')
}
// Get relative path in relation to root of gitrepo
rel_path := abs_path[repo_path.len + 1..] // +1 to skip the trailing slash
rel_path := abs_path[repo_path.len + 1..]
if !os.exists(abs_path) {
return error('Path does not exist inside the repository: ${abs_path}')
}
mut branch_or_tag := gs.status_wanted.branch
if gs.status_wanted.tag.len > 0 {
branch_or_tag = gs.status_wanted.tag
mut branch_or_tag := repo.status.branch
if repo.status.tag != '' {
branch_or_tag = repo.status.tag
}
return GitLocation{
provider: gs.provider
account: gs.account
name: gs.name
provider: repo.provider
account: repo.account
name: repo.name
branch_or_tag: branch_or_tag
path: rel_path // relative path in relation to git repo
path: rel_path
}
}
// Check if repo path exists and validate fields
// init validates the repository's configuration and path.
pub fn (mut repo GitRepo) init() ! {
path_string := repo.path()
if repo.provider == '' {
return error('Provider cannot be empty')
}
if repo.account == '' {
return error('Account cannot be empty')
}
if repo.name == '' {
return error('Name cannot be empty')
if repo.provider == '' || repo.account == '' || repo.name == '' {
return error('Repo identifier (provider, account, name) cannot be empty for ${repo.path()}')
}
if !os.exists(path_string) {
return error('Path does not exist: ${path_string}')
}
// Check if deploy key is set in repo config
if repo.deploysshkey.len > 0 {
git_config := repo.exec('git config --get core.sshCommand') or { '' }
if !git_config.contains(repo.deploysshkey) {
repo.set_sshkey(repo.deploysshkey)!
}
}
// Check that either tag or branch is set on wanted, but not both
if repo.status_wanted.tag.len > 0 && repo.status_wanted.branch.len > 0 {
return error('Cannot set both tag and branch in wanted status. Choose one or the other.')
if !os.exists(repo.path()) {
return error('Path does not exist: ${repo.path()}')
}
}
// Set the ssh key on the repo
// set_sshkey configures the repository to use a specific SSH key for git operations.
fn (mut repo GitRepo) set_sshkey(key_name string) ! {
// will use this dir to find and set key from
ssh_dir := os.join_path(os.home_dir(), '.ssh')
key := osal.get_ssh_key(key_name, directory: ssh_dir) or {
return error('SSH Key with name ${key_name} not found.')
}
private_key := key.private_key_path()!
repo.exec('git config core.sshcommand "ssh -i ~/.ssh/${private_key.path}"')!
private_key_path := key.private_key_path()!
repo.exec('git config core.sshCommand "ssh -i ${private_key_path}"')!
repo.deploysshkey = key_name
}
// Removes all changes from the repo; be cautious
// remove_changes hard resets the repository to HEAD and cleans untracked files.
pub fn (mut repo GitRepo) remove_changes() ! {
repo.status_update()!
if repo.has_changes {
console.print_header('Removing changes in ${repo.path()}')
repo.exec('git reset HEAD --hard && git clean -xfd') or {
return error("can't remove changes on repo: ${repo.path()}.\n${err}")
// TODO: we can do this fall back later
// console.print_header('Could not remove changes; will re-clone ${repo.path()}')
// mut p := repo.patho()!
// p.delete()! // remove path, this will re-clone the full thing
// repo.load_from_url()!
}
repo.cache_last_load_clear()! // MODIFIED: Invalidate cache instead of full reload.
if repo.status.has_changes {
console.print_header('Removing all local changes in ${repo.path()}')
repo.exec('git reset --hard HEAD && git clean -fdx')!
repo.cache_last_load_clear()!
}
}
// alias for remove changes
// reset is an alias for remove_changes.
pub fn (mut repo GitRepo) reset() ! {
return repo.remove_changes()
repo.remove_changes()!
}
// Update submodules
// update_submodules initializes and updates all submodules.
fn (mut repo GitRepo) update_submodules() ! {
repo.exec('git submodule update --init --recursive') or {
return error('Cannot update submodules for repo: ${repo.path()}. Error: ${err}')
}
repo.exec('git submodule update --init --recursive')!
}
// exec executes a command within the repository's directory.
// This is the designated wrapper for all git commands for this repo.
fn (repo GitRepo) exec(cmd_ string) !string {
repo_path := repo.path()
cmd := 'cd ${repo_path} && ${cmd_}'
// console.print_debug(cmd)
r := os.execute(cmd)
if r.exit_code != 0 {
return error('Repo failed to exec cmd: ${cmd}\n${r.output})')
return error('Repo command failed:\nCMD: ${cmd}\nOUT: ${r.output})')
}
return r.output
return r.output.trim_space()
}

View File

@@ -2,6 +2,7 @@ module gittools
import freeflowuniverse.herolib.ui.console
import os
import freeflowuniverse.herolib.core.pathlib
@[params]
pub struct GitCloneArgs {
@@ -23,7 +24,20 @@ pub fn (mut gitstructure GitStructure) clone(args GitCloneArgs) !&GitRepo {
// gitlocatin comes just from the url, not from fs of whats already there
git_location := gitstructure.gitlocation_from_url(args.url)!
mut repo := gitstructure.repo_new_from_gitlocation(git_location)!
// Initialize a new GitRepo instance
mut repo := GitRepo{
gs: &gitstructure
provider: git_location.provider
account: git_location.account
name: git_location.name
deploysshkey: args.sshkey // Use the sshkey from args
config: GitRepoConfig{} // Initialize with default config
status: GitStatus{} // Initialize with default status
}
// Add the new repo to the gitstructure's repos map
key_ := repo.cache_key()
gitstructure.repos[key_] = &repo
mut repopath := repo.patho()!
if repopath.exists() {
@@ -56,13 +70,11 @@ pub fn (mut gitstructure GitStructure) clone(args GitCloneArgs) !&GitRepo {
if result.exit_code != 0 {
return error('Cannot clone the repository due to: \n${result.output}')
}
repo.load()!
if repo.need_checkout() {
repo.checkout()!
}
// The repo is now cloned. Load its initial status.
repo.load_internal()!
console.print_green("The repository '${repo.name}' cloned into ${parent_dir}.")
return repo
return &repo // Return the initialized repo
}

View File

@@ -1,78 +1,37 @@
// lib/develop/gittools/repository_info.v
module gittools
// Check if there are staged changes to commit.
// need_commit checks if there are staged or unstaged changes.
pub fn (mut repo GitRepo) need_commit() !bool {
// This function assumes `status_update` has already been called.
return repo.has_changes
repo.status_update()!
return repo.status.has_changes
}
// Check if the repository has local changes that need to be pushed to remote
// need_push checks if the repository has local commits that need to be pushed.
pub fn (mut repo GitRepo) need_push() !bool {
// This function assumes `status_update` has already run to populate the status.
// A new local branch that doesn't exist on the remote needs to be pushed.
if repo.status_local.branch != '' && repo.get_last_remote_commit()! == '' {
return true
}
// If the local branch is ahead of its remote counterpart, it needs to be pushed.
return repo.status_local.ahead > 0
repo.status_update()!
return repo.status.ahead > 0
}
// Check if the repository needs to pull changes from remote
// need_pull checks if the repository needs to pull changes from the remote.
pub fn (mut repo GitRepo) need_pull() !bool {
// This function assumes `status_update` has already run to populate the status.
// If the local branch is behind its remote counterpart, it needs to be pulled.
return repo.status_local.behind > 0
repo.status_update()!
return repo.status.behind > 0
}
// Legacy function for backward compatibility
// need_push_or_pull is a convenience function.
pub fn (mut repo GitRepo) need_push_or_pull() !bool {
// This function relies on the simplified need_push() and need_pull() checks.
repo.status_update()!
return repo.need_push()! || repo.need_pull()!
}
// Determine if the repository needs to checkout to a different branch or tag
fn (mut repo GitRepo) need_checkout() bool {
if repo.status_wanted.branch.len > 0 {
if repo.status_wanted.branch != repo.status_local.branch {
return true
}
} else if repo.status_wanted.tag.len > 0 {
if repo.status_wanted.tag != repo.status_local.tag {
return true
}
}
// it could be empty since the status_wanted are optional.
// else{
// panic("bug, should never be empty ${repo.status_wanted.branch}, ${repo.status_local.branch}")
// }
return false
}
fn (mut repo GitRepo) get_remote_default_branchname() !string {
if repo.status_remote.ref_default.len == 0 {
return error('ref_default cannot be empty for ${repo.path()}')
}
return repo.status_remote.branches[repo.status_remote.ref_default] or {
return error("can't find ref_default in branches for ${repo.path()}")
}
}
// is always the commit for the branch as known remotely, if not known will return ""
// get_last_remote_commit gets the commit hash for the current branch as known on the remote.
pub fn (self GitRepo) get_last_remote_commit() !string {
if self.status_local.branch in self.status_remote.branches {
return self.status_remote.branches[self.status_local.branch]
}
return ''
// The branch map contains both local and remote refs, normalized by name.
return self.status.branches[self.status.branch] or { '' }
}
// get commit for branch, will return '' if local branch doesn't exist remotely
// get_last_local_commit gets the commit hash for the current local branch.
pub fn (self GitRepo) get_last_local_commit() !string {
if self.status_local.branch in self.status_local.branches {
return self.status_local.branches[self.status_local.branch]
}
return ''
return self.exec('git rev-parse HEAD')!
}

View File

@@ -24,8 +24,8 @@ pub fn (mut repo GitRepo) status_update(args StatusUpdateArgs) ! {
|| current_time - repo.last_load >= repo.config.remote_check_period {
repo.load_internal() or {
// Persist the error state to the cache
if repo.status_remote.error == '' {
repo.status_remote.error = 'Failed to load repository: ${err}'
if repo.status.error == '' {
repo.status.error = 'Failed to load repository: ${err}'
}
repo.cache_set()!
return error('Failed to load repository ${repo.name}: ${err}')
@@ -40,15 +40,15 @@ fn (mut repo GitRepo) load_internal() ! {
repo.init()!
repo.exec('git fetch --all') or {
repo.status_remote.error = 'Failed to fetch updates: ${err}'
repo.status.error = 'Failed to fetch updates: ${err}'
return error('Failed to fetch updates for ${repo.name} at ${repo.path()}: ${err}. Please check network connection and repository access.')
}
repo.load_branches()!
repo.load_tags()!
// Reset ahead/behind counts before recalculating
repo.status_local.ahead = 0
repo.status_local.behind = 0
repo.status.ahead = 0
repo.status.behind = 0
// Get ahead/behind information for the current branch
status_res := repo.exec('git status --porcelain=v2 --branch')!
@@ -59,10 +59,10 @@ fn (mut repo GitRepo) load_internal() ! {
ahead_str := parts[2]
behind_str := parts[3]
if ahead_str.starts_with('+') {
repo.status_local.ahead = ahead_str[1..].int()
repo.status.ahead = ahead_str[1..].int()
}
if behind_str.starts_with('-') {
repo.status_local.behind = behind_str[1..].int()
repo.status.behind = behind_str[1..].int()
}
}
break // We only need this one line
@@ -71,13 +71,17 @@ fn (mut repo GitRepo) load_internal() ! {
repo.last_load = int(time.now().unix())
repo.has_changes = repo.detect_changes() or {
repo.status_local.error = 'Failed to detect changes: ${err}'
repo.status.has_changes = repo.detect_changes() or {
repo.status.error = 'Failed to detect changes: ${err}'
return error('Failed to detect changes in repository ${repo.name}: ${err}')
}
// Persist the newly loaded state to the cache.
repo.cache_set()!
println(repo)
$dbg;
}
// Helper to load remote tags
@@ -99,13 +103,13 @@ fn (mut repo GitRepo) load_branches() ! {
if name.contains('_archive') {
continue
} else if name == 'origin' {
repo.status_remote.ref_default = commit_hash
// No longer storing ref_default separately, it's implied by the branch map
} else if name.starts_with('origin') {
name = name.all_after('origin/').trim_space()
// Update remote tags info
repo.status_remote.branches[name] = commit_hash
// Update branches info
repo.status.branches[name] = commit_hash
} else {
repo.status_local.branches[name] = commit_hash
repo.status.branches[name] = commit_hash
}
}
}
@@ -114,7 +118,7 @@ fn (mut repo GitRepo) load_branches() ! {
return error('Failed to get current branch: ${err}')
}.split_into_lines().filter(it.trim_space() != '')
if mybranch.len == 1 {
repo.status_local.branch = mybranch[0].trim_space()
repo.status.branch = mybranch[0].trim_space()
} else {
return error('bug: git branch does not give branchname.\n${mybranch}')
}
@@ -139,7 +143,7 @@ fn (mut repo GitRepo) load_tags() ! {
tag_name := parts[1].trim_space()
// Update remote tags info
repo.status_remote.tags[tag_name] = commit_hash
repo.status.tags[tag_name] = commit_hash
}
}
}