Skip to content

develop.gittools #

Git Tools Module

GitTools HeroScript

!!git.define

Configure or retrieve a GitStructure, if not set will use the default

!!git.define
    coderoot:'/tmp/code' //when we overrule the location, the default is ~/code
    light:true //depth of git clone is 1
    log:true 
    debug:false //give more error reporting
    offline:false //makes sure will not try to get to internet, but do all locally
    ssh_key_path:'' //if a specific ssh key is needed
    reload:false //if set then will remove cache and load full status, this is slow !

!!git.clone

Clones a Git repository from a specified URL into the configured coderoot.

!!git.clone
 url: 'https://github.com/incubaid/test_repo.git'
    pull: true // Optional: if true, pulls latest changes after cloning
    reset: false // Optional: if true, resets the repository before cloning/pulling
 light: true // Optional: if true, clones only the last history (default: is from git structure as defined above)
 recursive: false // Optional: if true, also clones submodules (default: false)

!!git.repo_action

Performs various Git operations on an existing repository, the filter matches more than 1 repo, gives error if none found

!!git.repo_action 
    filter: 'incubaid/test_repo'
 action: 'pull' // pull, commit, push, reset, branch_create, branch_switch, tag_create, tag_switch, delete
 message: 'feat: Added new feature' // Optional: for 'commit' action
 branchname: 'feature-branch' // Optional: for 'branch_create' or 'branch_switch' actions
 tagname: 'v1.0.0' // Optional: for 'tag_create' or 'tag_switch' actions
 submodules: true // Optional: for 'pull' action, if true, also updates submodules
    error_ignore: false // Optional: if true, ignores errors during the action and continue for the next repo

Parameters:

  • filter (string, required): A substring to filter repositories by name or relative path. This can match multiple repositories.
  • action (string, required): The Git operation to perform. Valid values:
  • pull: Pulls latest changes from the remote.
  • commit: Commits staged changes. Requires message.
  • push: Pushes local commits to the remote.
  • reset: Resets all local changes (hard reset).
  • branch_create: Creates a new branch. Requires branchname.
  • branch_switch: Switches to an existing branch. Requires branchname.
  • tag_create: Creates a new tag. Requires tagname.
  • tag_switch: Switches to an existing tag. Requires tagname.
  • delete: Deletes the local repository.

!!git.list

Lists known Git repositories managed by the gittools module.

!!git.list
 filter: 'my_project' // Optional: filter by repository name or path
    reload: true //if true then will check the status of those repo's against the remote's

!!git.reload

Forces a reload of all Git repositories in the cache, re-scanning the coderoot and updating their statuses.

!!git.reload
 filter: 'my_project' // Optional: filter by repository name or path

Get a specific path starting from url

below is powerful command, will get the repo, put on right location, you can force a pull or even reset everything

import incubaid.herolib.develop.gittools
//  path      string
//  git_url   string
//  git_reset bool
//  git_root  string
//  git_pull  bool
//  currentdir bool // can use currentdir, if true, will use current directory as base path if not giturl or path specified
mydocs_path:=gittools.path(
    pull:true,
    git_url:'https://git.threefold.info/tfgrid/info_docs_depin/src/branch/main/docs'
)!

println(mydocs_path)

//the returned path is from pathlib, so its easy to further process

//more complete example

@[params]
pub struct GitPathGetArgs {
pub mut:
    someotherparams string // you can add other params here if you want
    //gittools will use these params to find the right path
 path      string
 git_url   string
 git_reset bool
 git_root  string
 git_pull  bool
}
pub fn something(args GitPathGetArgs) !string{
    mut path := gittools.path(path: args.path, git_url: args.git_url, git_reset: args.git_reset, git_root: args.git_root, git_pull: args.git_pull)!
 if !path.is_dir() {
  return error('path is not a directory')
 }
 if path.file_exists('.site') {
  move_site_to_collection(mut path)!
 }
    return path.path
}

Repository Management

import incubaid.herolib.develop.gittools

// Initialize with code root directory
mut gs := gittools.new(coderoot: '~/code')!

// Clone a repository
mut repo := gs.clone(GitCloneArgs{
    url: 'git@github.com:username/repo.git'
    sshkey: 'deploy_key'  // Optional SSH key name
})!

// Or get existing repository
mut repo := gs.get_repo(name: 'existing_repo')!

// Delete repository
repo.delete()!

Branch Operations

// Create and switch to new branch
repo.branch_create('feature-branch')!
repo.branch_switch('feature-branch')!

// Check status and commit changes
if repo.has_changes {
    repo.commit('feat: Add new feature')!
    repo.push()!
}

// Pull latest changes
repo.pull()!

// Pull with submodules
repo.pull(submodules: true)!

Tag Management

// Create a new tag
repo.tag_create('v1.0.0')!

// Switch to tag
repo.tag_switch('v1.0.0')!

// Check if tag exists
exists := repo.tag_exists('v1.0.0')!

// Get tag information
if repo.status_local.tag == 'v1.0.0' {
    // Currently on tag v1.0.0
}

Advanced Features

SSH Key Integration

// Clone with SSH key
mut repo := gs.clone(GitCloneArgs{
    url: 'git@github.com:username/repo.git'
    sshkey: 'deploy_key'
})!

// Set SSH key for existing repository
repo.set_sshkey('deploy_key')!

Repository Status

// Update repository status
repo.status_update()!

// Check various status conditions
if repo.need_commit() {
    // Has uncommitted changes
}

if repo.need_push_or_pull() {
    // Has unpushed/unpulled changes
}

if repo.need_checkout() {
    // Needs to checkout different branch/tag
}

Change Management

// Check for changes
if repo.has_changes {
    // Handle changes
}

// Reset all changes
repo.reset()!
// or
repo.remove_changes()!

// Update submodules
repo.update_submodules()!

Repository Configuration & Status

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

// GitRepo represents a single git repository.
pub struct GitRepo {
pub mut:
    provider      string
    account       string
    name          string
    config        GitRepoConfig
    status        GitStatus   // Unified struct holding the CURRENT repo status.
}

// GitStatus holds all live status information for a repository.
pub struct GitStatus {
pub mut:
 // 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
}

Error Handling

The module provides comprehensive error handling:

// Clone with error handling
mut repo := gs.clone(url: 'invalid_url') or {
    println('Clone failed: ${err}')
    return
}

// Commit with error handling
repo.commit('feat: New feature') or {
    if err.msg().contains('nothing to commit') {
        println('No changes to commit')
    } else {
        println('Commit failed: ${err}')
    }
    return
}

Testing

Run the test suite:

v -enable-globals test herolib/develop/gittools/tests/

Notes

  • SSH keys should be properly configured in ~/.ssh/
  • For readonly repositories, all local changes will be reset on pull
  • Light cloning option (config.light: true) creates shallow clones
  • Repository status is automatically cached and updated
  • Submodules are handled recursively when specified
  • All operations maintain repository consistency

Constants #

const gitcmds = 'clone,commit,pull,push,delete,reload,list,edit,sourcetree,path,exists,check,lfs'

fn new #

fn new(args_ GitStructureArgsNew) !&GitStructure

Retrieve or create a new GitStructure instance with the given configuration.

fn path #

fn path(args_ GitPathGetArgs) !pathlib.Path

return pathlib Path based on, will pull... params: path string git_url string git_reset bool git_root string git_pull bool

fn reset #

fn reset()

struct GitCloneArgs #

@[params]
struct GitCloneArgs {
pub mut:
	// only url needed because is a clone
	url       string
	sshkey    string
	recursive bool // If true, also clone submodules
	light     bool // If true, clones only the last history for all branches (clone with only 1 level deep)
}

struct GitLocation #

@[heap]
struct GitLocation {
pub mut:
	provider      string // Git provider (e.g., GitHub)
	account       string // Account name
	name          string // Repository name
	branch_or_tag string // Branch name
	path          string // Path in the repository (not the filesystem)
	anker         string // Position in a file
}

GitLocation uniquely identifies a Git repository, its online URL, and its location in the filesystem.

struct GitPathGetArgs #

@[params]
struct GitPathGetArgs {
pub mut:
	path       string
	git_url    string
	git_reset  bool
	git_root   string
	git_pull   bool
	currentdir bool // can use currentdir
}

struct GitRepo #

@[heap]
struct GitRepo {
	// a git repo is always part of a git structure
mut:
	gs        &GitStructure @[skip; str: skip]
	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
}

GitRepo represents a single git repository.

fn (GitRepo) branch_create #

fn (mut repo GitRepo) branch_create(branchname string) !

branch_create creates a new branch.

fn (GitRepo) branch_switch #

fn (mut repo GitRepo) branch_switch(branchname string) !

branch_switch switches to a different branch.

fn (GitRepo) cache_key #

fn (mut repo GitRepo) cache_key() string

get the key in redis where json cached info is

fn (GitRepo) check #

fn (mut repo GitRepo) check() !

fn (GitRepo) commit #

fn (mut repo GitRepo) commit(msg string) !

commit stages all changes and commits them with the provided message.

fn (GitRepo) delete #

fn (mut repo GitRepo) delete() !

delete removes the repository from the filesystem and cache.

fn (GitRepo) detect_changes #

fn (mut repo GitRepo) detect_changes() !bool

Check if there are any unstaged or untracked changes in the repository.

fn (GitRepo) display_current_status #

fn (mut repo GitRepo) display_current_status() !

Return rich path object from our library hero lib

fn (GitRepo) exists #

fn (repo GitRepo) exists() bool

Check if repository exists at its expected path

fn (GitRepo) get_changes_staged #

fn (repo GitRepo) get_changes_staged() ![]string

Retrieves a list of staged changes in the repository.

This function returns a list of files that are staged and ready to be committed.

Returns:- An array of strings representing file paths of staged changes.

  • Throws an error if the command execution fails.

fn (GitRepo) get_changes_unstaged #

fn (repo GitRepo) get_changes_unstaged() ![]string

Retrieves a list of unstaged changes in the repository.

This function returns a list of files that are modified or untracked.

Returns:- An array of strings representing file paths of unstaged changes.

  • Throws an error if the command execution fails.

fn (GitRepo) get_human_path #

fn (repo GitRepo) get_human_path() !string

path where we use ~ and its the full path

fn (GitRepo) get_last_local_commit #

fn (self GitRepo) get_last_local_commit() !string

get_last_local_commit gets the commit hash for the current local branch.

fn (GitRepo) get_last_remote_commit #

fn (self GitRepo) get_last_remote_commit() !string

get_last_remote_commit gets the commit hash for the current branch as known on the remote.

fn (GitRepo) get_parent_dir #

fn (mut repo GitRepo) get_parent_dir(args GetParentDir) !string

fn (GitRepo) get_path_of_url #

fn (mut repo GitRepo) get_path_of_url(url string) !string

gets the path of a given url within a repo ex: 'https://git.threefold.info/ourworld_holding/info_ourworld/src/branch/main/books/cocreation/SUMMARY.md' returns <repo_path>/books/cocreation/SUMMARY.md

fn (GitRepo) get_relative_path #

fn (repo GitRepo) get_relative_path() !string

Relative path inside the gitstructure, pointing to the repo

fn (GitRepo) gitlocation_from_path #

fn (mut repo GitRepo) gitlocation_from_path(path string) !GitLocation

gitlocation_from_path creates a GitLocation from a path inside this repository.

fn (GitRepo) init #

fn (mut repo GitRepo) init() !

init validates the repository's configuration and path.

fn (GitRepo) lfs #

fn (mut repo GitRepo) lfs() !bool

Check if repo has lfs enabled

fn (GitRepo) lfs_check #

fn (mut repo GitRepo) lfs_check() !

check if repo has lfs enabled

fn (GitRepo) lfs_init #

fn (mut repo GitRepo) lfs_init() !

fn (GitRepo) need_commit #

fn (mut repo GitRepo) need_commit() !bool

need_commit checks if there are staged or unstaged changes.

fn (GitRepo) need_pull #

fn (mut repo GitRepo) need_pull() !bool

need_pull checks if the repository needs to pull changes from the remote.

fn (GitRepo) need_push #

fn (mut repo GitRepo) need_push() !bool

need_push checks if the repository has local commits that need to be pushed.

fn (GitRepo) need_push_or_pull #

fn (mut repo GitRepo) need_push_or_pull() !bool

need_push_or_pull is a convenience function.

fn (GitRepo) open_vscode #

fn (mut repo GitRepo) open_vscode() !

Opens Visual Studio Code for the repo

fn (GitRepo) path #

fn (repo GitRepo) path() string

get path where the repo is on the fs

fn (GitRepo) patho #

fn (repo GitRepo) patho() !pathlib.Path

get herolib path object

fn (GitRepo) print_key #

fn (mut repo GitRepo) print_key() string

fn (GitRepo) pull #

fn (mut repo GitRepo) pull(args PullArgs) !

pull remote content into the repository.

fn (GitRepo) push #

fn (mut repo GitRepo) push() !

push local changes to the remote repository.

fn (GitRepo) remove_changes #

fn (mut repo GitRepo) remove_changes() !

remove_changes hard resets the repository to HEAD and cleans untracked files.

fn (GitRepo) reset #

fn (mut repo GitRepo) reset() !

reset is an alias for remove_changes.

fn (GitRepo) sourcetree #

fn (mut repo GitRepo) sourcetree() !

Opens SourceTree for the Git repo

fn (GitRepo) status_update #

fn (mut repo GitRepo) status_update(args StatusUpdateArgs) !

fn (GitRepo) tag_create #

fn (mut repo GitRepo) tag_create(tagname string) !

tag_create creates a new tag.

fn (GitRepo) tag_exists #

fn (mut repo GitRepo) tag_exists(tag string) !bool

tag_exists checks if a tag exists in the repository.

fn (GitRepo) tag_switch #

fn (mut repo GitRepo) tag_switch(tagname string) !

tag_switch checks out a specific tag.

struct GitRepoConfig #

struct GitRepoConfig {
pub mut:
	remote_check_period int = 3600 * 24 * 7 // seconds = 7d
}

struct GitStatus #

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.
}

GitStatus holds the unified status information for a repository. It reflects the CURRENT state, not a desired state.

struct GitStructure #

@[heap]
struct GitStructure {
mut:
	config_ ?GitStructureConfig // Configuration settings for the git structure.
pub mut:
	key      string              // Unique key representing the git structure (default is hash of $home/code).	
	repos    map[string]&GitRepo // Map of repositories
	coderoot pathlib.Path
	log      bool = true // If true, logs git commands/statements
	debug    bool = true
	offline  bool
}

GitStructure holds information about repositories within a specific code root. This structure keeps track of loaded repositories, their configurations, and their status.

fn (GitStructure) cache_key #

fn (mut self GitStructure) cache_key() string

key in redis used to store all config info

fn (GitStructure) cache_load #

fn (mut self GitStructure) cache_load() !

load from cache

fn (GitStructure) cache_reset #

fn (mut self GitStructure) cache_reset() !

Reset all caches and configurations for all Git repositories.

fn (GitStructure) check_repos_exist #

fn (mut gs GitStructure) check_repos_exist(args ReposActionsArgs) !string

Check if any repositories exist based on filter criteria and return result for exists command

fn (GitStructure) clone #

fn (mut gitstructure GitStructure) clone(args GitCloneArgs) !&GitRepo

Clones a new repository into the git structure based on the provided arguments.

fn (GitStructure) config #

fn (mut self GitStructure) config() !GitStructureConfig

Load config from redis

fn (GitStructure) config_set #

fn (mut self GitStructure) config_set(args GitStructureConfig) !

fn (GitStructure) do #

fn (mut gs GitStructure) do(args_ ReposActionsArgs) !string

do group actions on repo args

 cmd      string // clone,commit,pull,push,delete,reload,list,edit,sourcetree,cd
 filter   string // if used will only show the repo's which have the filter string inside
 repo     string
 account  string
 provider string
 msg      string
 url      string
 pull     bool
 reload bool //means reload the info into the cache
 script   bool = true // run non interactive
 reset    bool = true // means we will lose changes (only relevant for clone, pull)

fn (GitStructure) get_path #

fn (mut gitstructure GitStructure) get_path(args_ ReposGetArgs) !string

Retrieves a single repository path based on the provided arguments (goes inside repo). if pull will force a pull, if it can't will be error, if reset will remove the changes If the repository does not exist, it will clone it

Args:

 ReposGetArgs {
 name     string // Specific repository name to retrieve.
 account  string // Git account associated with the repository.
 provider string // Git provider (e.g., GitHub).
 pull     bool   // Pull the last changes.
 reset    bool   // Reset the changes.
 url      string // Repository URL, used if cloning is needed.

Returns:- &GitRepo: Reference to the retrieved or cloned repository.

Raises:- Error: If multiple repositories are found with similar names or if cloning fails.

fn (GitStructure) get_repo #

fn (mut gitstructure GitStructure) get_repo(args_ ReposGetArgs) !&GitRepo

Retrieves a single repository based on the provided arguments. if pull will force a pull, if it can't will be error, if reset will remove the changes If the repository does not exist, it will clone it

Args:

 ReposGetArgs {
 filter   string // Optional filter for repository names
 name     string // Specific repository name to retrieve.
 account  string // Git account associated with the repository.
 provider string // Git provider (e.g., GitHub).
 pull     bool   // Pull the last changes.
 reset    bool   // Reset the changes.
 url      string // Repository URL, used if cloning is needed.

Returns:- &GitRepo: Reference to the retrieved or cloned repository.

Raises:- Error: If multiple repositories are found with similar names or if cloning fails.

fn (GitStructure) get_repos #

fn (mut gitstructure GitStructure) get_repos(args_ ReposGetArgs) ![]&GitRepo

Retrieves a list of repositories from the git structure that match the provided arguments. if pull will force a pull, if it can't will be error, if reset will remove the changes

Args:

 ReposGetArgs {
 filter   string // Optional filter for repository names
 name     string // Specific repository name to retrieve.
 account  string // Git account associated with the repository.
 provider string // Git provider (e.g., GitHub).
    pull     bool   // Pull the last changes.
    reset    bool   // Reset the changes
 status_clean   bool   //make sure each cache status is but on 0, if we also do status_update this will result in a reload
 status_update bool //make sure each repo get's status updated
 url      string // Repository URL, used if cloning is needed.

Returns:- []&GitRepo: A list of repository references that match the criteria.

fn (GitStructure) get_working_repo #

fn (mut gitstructure GitStructure) get_working_repo() ?GitRepo

returns the git repository of the working directory by locating the parent directory with .git.

Returns:- GitRepo: Reference to the initialized repository.

Raises:- None: If .git is not found in the parent directories.

fn (GitStructure) gitlocation_from_path #

fn (mut gs GitStructure) gitlocation_from_path(path string) !GitLocation

Get GitLocation from a path within the Git repository, doesn't read from fs, is just from path missing branch...

fn (GitStructure) gitlocation_from_url #

fn (mut gs GitStructure) gitlocation_from_url(url string) !GitLocation

Get GitLocation from a URL, doesn't go on filesystem just tries to figure out branch, ...

fn (GitStructure) load #

fn (mut gitstructure GitStructure) load(reset bool) !

Loads all repository information from the filesystem and updates from remote if necessary. Use the reset argument to force reloading from the disk.

fn (GitStructure) repos_print #

fn (mut gitstructure GitStructure) repos_print(args ReposGetArgs) !

Print repositories based on the provided criteria, showing their statuses

struct GitStructureArgsNew #

@[params]
struct GitStructureArgsNew {
pub mut:
	coderoot string
	log      bool = true // If true, logs git commands/statements
	debug    bool = true
	reload   bool
	offline  bool
}

struct GitStructureConfig #

@[params]
struct GitStructureConfig {
pub mut:
	light        bool = true // If true, clones only the last history for all branches (clone with only 1 level deep)
	ssh_key_name string
	ssh_key_path string
}

struct PullArgs #

@[params]
struct PullArgs {
pub mut:
	submodules bool // if we want to pull for submodules
	reset      bool // if true, will reset local changes before pulling
}

struct RepoInitParams #

@[params]
struct RepoInitParams {
	ssh_key_name string // name of ssh key to be used in repo
}

struct ReposActionsArgs #

@[params]
struct ReposActionsArgs {
pub mut:
	cmd       string // clone,commit,pull,push,delete,reload,list,edit,sourcetree
	filter    string // if used will only show the repo's which have the filter string inside
	repo      string
	account   string
	provider  string
	msg       string
	url       string
	branch    string
	path      string // path to start from
	recursive bool
	pull      bool
	reload    bool // means reload the info into the cache
	script    bool = true // run non interactive
	reset     bool = true // means we will lose changes (only relevant for clone, pull)
}

struct ReposGetArgs #

@[params]
struct ReposGetArgs {
pub mut:
	filter        string // Optional filter for repository names
	name          string // Specific repository name to retrieve.
	account       string // Git account associated with the repository.
	provider      string // Git provider (e.g., GitHub).
	pull          bool   // Pull the last changes.
	reset         bool   // Reset the changes.
	status_clean  bool   // make sure each cache status is but on 0, if we also do status_update this will result in a reload
	status_update bool   // make sure each repo get's status updated
	url           string // Repository URL
}

ReposGetArgs defines arguments to retrieve repositories from the git structure. It includes filters by name, account, provider, and an option to clone a missing repo.

struct StatusUpdateArgs #

@[params]
struct StatusUpdateArgs {
	reset bool
}