diff --git a/bash_modules.d/sentinel_git_tui.module b/bash_modules.d/sentinel_git_tui.module new file mode 100755 index 0000000..c6d331e --- /dev/null +++ b/bash_modules.d/sentinel_git_tui.module @@ -0,0 +1,225 @@ +#!/usr/bin/env bash +# SENTINEL MODULE +# Name: Git TUI +# ID: sentinel_git_tui +# Version: 0.1.0 +# Description: Minimal Git control panel TUI (status, diff, commit, pull, push, log, branches, stash, submodules) +# Requires: git +# Category: vcs + +# Prevent double loading +if [[ -n "${_SENTINEL_GIT_TUI_LOADED:-}" ]]; then + return 0 +fi +export _SENTINEL_GIT_TUI_LOADED=1 + +# Optional: if SENTINEL has a registration API, hook into it +if command -v sentinel_register_app >/dev/null 2>&1; then + sentinel_register_app "Git TUI" "sentinel_git_tui_main" "Git / VCS" +fi + +_sent_git_color() { + local code="$1"; shift + printf "\033[%sm%s\033[0m" "$code" "$*" +} + +_sent_git_header() { + echo + _sent_git_color "1;36" "=== Git TUI :: ${PWD} ===" + echo +} + +_sent_git_press_enter() { + echo + read -rp "Press Enter to continue..." _ +} + +_sent_git_check_repo() { + if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then + _sent_git_color "1;31" "[!] Not inside a Git repository." + echo + return 1 + fi + return 0 +} + +_sent_git_status() { + _sent_git_header + git status + _sent_git_press_enter +} + +_sent_git_diff() { + _sent_git_header + git diff | "${PAGER:-less}" +} + +_sent_git_log() { + _sent_git_header + git log --oneline --graph --decorate --all | "${PAGER:-less}" +} + +_sent_git_branches() { + _sent_git_header + git branch -a + _sent_git_press_enter +} + +_sent_git_checkout() { + _sent_git_header + git branch -a + echo + read -rp "Branch to checkout: " br + [ -z "$br" ] && return 0 + git checkout "$br" + _sent_git_press_enter +} + +_sent_git_commit() { + _sent_git_header + git status + echo + _sent_git_color "1;33" "Commit all tracked changes (git commit -am)?" + echo + read -rp "Commit message: " msg + [ -z "$msg" ] && return 0 + git commit -am "$msg" + _sent_git_press_enter +} + +_sent_git_pull() { + _sent_git_header + _sent_git_color "1;33" "Pull from default remote (git pull --ff-only)" + echo + read -rp "Proceed? [y/N]: " ans + case "$ans" in + y|Y) git pull --ff-only ;; + *) echo "Skipped." ;; + esac + _sent_git_press_enter +} + +_sent_git_push() { + _sent_git_header + _sent_git_color "1;33" "Push to default remote (git push)" + echo + read -rp "Proceed? [y/N]: " ans + case "$ans" in + y|Y) git push ;; + *) echo "Skipped." ;; + esac + _sent_git_press_enter +} + +_sent_git_submodules() { + _sent_git_header + _sent_git_color "1;33" "Update submodules (git submodule update --init --recursive)" + echo + read -rp "Proceed? [y/N]: " ans + case "$ans" in + y|Y) git submodule update --init --recursive ;; + *) echo "Skipped." ;; + esac + _sent_git_press_enter +} + +_sent_git_stash_menu() { + while true; do + _sent_git_header + _sent_git_color "1;35" "[Stash menu]" + echo + echo "1) Stash current changes" + echo "2) List stashes" + echo "3) Apply latest stash" + echo "4) Drop latest stash" + echo "b) Back" + echo + read -rp "Choice: " s + case "$s" in + 1) + read -rp "Stash message (optional): " sm + if [ -z "$sm" ]; then + git stash + else + git stash push -m "$sm" + fi + _sent_git_press_enter + ;; + 2) + git stash list + _sent_git_press_enter + ;; + 3) + git stash apply + _sent_git_press_enter + ;; + 4) + git stash drop + _sent_git_press_enter + ;; + b|B|'') + break + ;; + *) + ;; + esac + done +} + +sentinel_git_tui_main() { + local repo_dir="${1:-$PWD}" + + if ! command -v git >/dev/null 2>&1; then + _sent_git_color "1;31" "[!] git is not installed or not in PATH." + echo + return 1 + fi + + if [ ! -d "$repo_dir/.git" ] && ! (cd "$repo_dir" && git rev-parse --is-inside-work-tree >/dev/null 2>&1); then + _sent_git_color "1;31" "[!] $repo_dir is not a Git repository." + echo + return 1 + fi + + cd "$repo_dir" || return 1 + + while true; do + _sent_git_header + _sent_git_check_repo || return 1 + + _sent_git_color "1;32" "Select an action:" + echo + echo "1) Status" + echo "2) Diff" + echo "3) Commit (all tracked)" + echo "4) Pull" + echo "5) Push" + echo "6) Log (graph)" + echo "7) Branches" + echo "8) Checkout branch" + echo "9) Submodules (init/update recursive)" + echo "s) Stash menu" + echo "q) Quit" + echo + read -rp "Choice: " c + case "$c" in + 1) _sent_git_status ;; + 2) _sent_git_diff ;; + 3) _sent_git_commit ;; + 4) _sent_git_pull ;; + 5) _sent_git_push ;; + 6) _sent_git_log ;; + 7) _sent_git_branches ;; + 8) _sent_git_checkout ;; + 9) _sent_git_submodules ;; + s|S) _sent_git_stash_menu ;; + q|Q|'') break ;; + *) ;; + esac + done +} + +# Allow direct invocation if sourced manually +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + sentinel_git_tui_main "$@" +fi diff --git a/install b/install new file mode 120000 index 0000000..61e71e2 --- /dev/null +++ b/install @@ -0,0 +1 @@ +install.sh \ No newline at end of file