Compare commits
4 Commits
v4.0.0
...
e567957ea0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e567957ea0 | ||
|
|
67bdd3e863 | ||
|
|
fa573bce8f | ||
|
|
241d53ebc4 |
16
README.md
16
README.md
@@ -58,8 +58,16 @@ A bar-style prompt showing current time, execution time of the last command
|
|||||||
| `findbig` | filefct | Find the biggest files in the given or current directory |
|
| `findbig` | filefct | Find the biggest files in the given or current directory |
|
||||||
| `finddead` | filefct | Find dead symbolic links in the given or current directory |
|
| `finddead` | filefct | Find dead symbolic links in the given or current directory |
|
||||||
| `findzero` | filefct | Find empty files in the given or current directory |
|
| `findzero` | filefct | Find empty files in the given or current directory |
|
||||||
|
| `gacp` | git | Add, commit and push changes; auto-pulls with rebase first if needed |
|
||||||
| `genpwd` | pwd | Generate one or more random secure passwords with configurable constraints |
|
| `genpwd` | pwd | Generate one or more random secure passwords with configurable constraints |
|
||||||
|
| `ggraph` | git | Display a decorated git history graph |
|
||||||
| `gpid` | processes | Give the list of PIDs matching the given process name(s) |
|
| `gpid` | processes | Give the list of PIDs matching the given process name(s) |
|
||||||
|
| `gprune` | git | Delete local branches already merged into the main branch |
|
||||||
|
| `greset` | git | Reset the current branch to upstream, stashing local changes first |
|
||||||
|
| `groot` | git | Display the repository root path, or change directory to it with `-g` |
|
||||||
|
| `gsync` | git | Fetch and rebase the current branch onto its upstream |
|
||||||
|
| `gst` | git | Display compact git status with branch tracking information |
|
||||||
|
| `gwip` | git | Create a quick WIP checkpoint commit |
|
||||||
| `help` | help | Display the list of available functions and basic usage |
|
| `help` | help | Display the list of available functions and basic usage |
|
||||||
| `isipv4` | net | Tell if the given parameter is a valid IPv4 address |
|
| `isipv4` | net | Tell if the given parameter is a valid IPv4 address |
|
||||||
| `isipv6` | net | Tell if the given parameter is a valid IPv6 address |
|
| `isipv6` | net | Tell if the given parameter is a valid IPv6 address |
|
||||||
@@ -171,6 +179,14 @@ change the default without having to pass flags every time.
|
|||||||
| `BUSY_DEFAULT_PATTERN` | `[0-9a-f]` | Hex pattern matched by `busy` |
|
| `BUSY_DEFAULT_PATTERN` | `[0-9a-f]` | Hex pattern matched by `busy` |
|
||||||
| `BUSY_DEFAULT_DELAY` | `0.1` | Polling delay (seconds) for `busy` |
|
| `BUSY_DEFAULT_DELAY` | `0.1` | Polling delay (seconds) for `busy` |
|
||||||
|
|
||||||
|
**`[git]`**
|
||||||
|
|
||||||
|
| Key | Default | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `GIT_MAIN_BRANCH` | `main` | Fallback main branch name used when remote HEAD cannot be detected |
|
||||||
|
| `GIT_DEFAULT_REMOTE` | `origin` | Default remote used by git helper functions |
|
||||||
|
| `GIT_WIP_PREFIX` | `wip` | Prefix used by `gwip` when generating automatic checkpoint messages |
|
||||||
|
|
||||||
**`[info]`**
|
**`[info]`**
|
||||||
|
|
||||||
| Key | Default | Description |
|
| Key | Default | Description |
|
||||||
|
|||||||
@@ -76,6 +76,17 @@ TERM=xterm-256color
|
|||||||
# busy: Delay between matched lines in milliseconds (0 = no delay).
|
# busy: Delay between matched lines in milliseconds (0 = no delay).
|
||||||
#BUSY_DEFAULT_DELAY=0
|
#BUSY_DEFAULT_DELAY=0
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
[git]
|
||||||
|
# Fallback main branch name used when remote HEAD cannot be detected.
|
||||||
|
#GIT_MAIN_BRANCH=main
|
||||||
|
|
||||||
|
# Default remote used by git helper functions.
|
||||||
|
#GIT_DEFAULT_REMOTE=origin
|
||||||
|
|
||||||
|
# Prefix used by gwip when generating automatic checkpoint messages.
|
||||||
|
#GIT_WIP_PREFIX=wip
|
||||||
|
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
[info]
|
[info]
|
||||||
# meteo: Default city when no argument is given. Leave unset to require an
|
# meteo: Default city when no argument is given. Leave unset to require an
|
||||||
|
|||||||
602
profile.d/git.sh
Executable file
602
profile.d/git.sh
Executable file
@@ -0,0 +1,602 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Copyright (c) 2013-2026 Geoffray Levasseur <fatalerrors@geoffray-levasseur.org>
|
||||||
|
# Protected by the BSD3 license. Please read bellow for details.
|
||||||
|
#
|
||||||
|
# * Redistribution and use in source and binary forms,
|
||||||
|
# * with or without modification, are permitted provided
|
||||||
|
# * that the following conditions are met:
|
||||||
|
# *
|
||||||
|
# * Redistributions of source code must retain the above
|
||||||
|
# * copyright notice, this list of conditions and the
|
||||||
|
# * following disclaimer.
|
||||||
|
# *
|
||||||
|
# * Redistributions in binary form must reproduce the above
|
||||||
|
# * copyright notice, this list of conditions and the following
|
||||||
|
# * disclaimer in the documentation and/or other materials
|
||||||
|
# * provided with the distribution.
|
||||||
|
# *
|
||||||
|
# * Neither the name of the copyright holder nor the names
|
||||||
|
# * of any other contributors may be used to endorse or
|
||||||
|
# * promote products derived from this software without
|
||||||
|
# * specific prior written permission.
|
||||||
|
# *
|
||||||
|
# * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||||
|
# * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
# * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
# * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
# * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
# * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
# * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
# * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
# * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
# * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
# * OF SUCH DAMAGE.
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Built-in defaults (can be overridden from [git] section in profile.conf)
|
||||||
|
: "${GIT_MAIN_BRANCH:=main}"
|
||||||
|
: "${GIT_DEFAULT_REMOTE:=origin}"
|
||||||
|
: "${GIT_WIP_PREFIX:=wip}"
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Internal helper: ensure git is available and cwd is a git worktree
|
||||||
|
_git_require_repo()
|
||||||
|
{
|
||||||
|
if ! command -v git >/dev/null 2>&1; then
|
||||||
|
disp E "git command not found."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
||||||
|
disp E "Current directory is not inside a git repository."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Internal helper: return default branch from remote HEAD, fallback to config
|
||||||
|
_git_default_branch()
|
||||||
|
{
|
||||||
|
local remote="${1:-$GIT_DEFAULT_REMOTE}"
|
||||||
|
local head
|
||||||
|
|
||||||
|
head=$(git symbolic-ref --quiet --short "refs/remotes/${remote}/HEAD" 2>/dev/null) || true
|
||||||
|
if [[ -n $head ]]; then
|
||||||
|
printf "%s\n" "${head#${remote}/}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf "%s\n" "$GIT_MAIN_BRANCH"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Display compact git status + branch tracking information
|
||||||
|
# Usage: gst [path]
|
||||||
|
gst()
|
||||||
|
{
|
||||||
|
local PARSED
|
||||||
|
PARSED=$(getopt -o h --long help -n 'gst' -- "$@")
|
||||||
|
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||||
|
if [[ $? -ne 0 ]]; then
|
||||||
|
disp E "Invalid options, use \"gst --help\" to display usage."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
eval set -- "$PARSED"
|
||||||
|
while true; do
|
||||||
|
case "$1" in
|
||||||
|
-h|--help)
|
||||||
|
printf "gst: Display short git status and branch tracking info.\n"
|
||||||
|
printf "Usage: gst [path]\n"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
disp E "Invalid options, use \"gst --help\" to display usage."
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
local target="${1:-.}"
|
||||||
|
git -C "$target" status --short --branch
|
||||||
|
}
|
||||||
|
export -f gst
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Show a readable commit graph
|
||||||
|
# Usage: ggraph [-n limit]
|
||||||
|
ggraph()
|
||||||
|
{
|
||||||
|
local PARSED
|
||||||
|
PARSED=$(getopt -o hn: --long help,limit: -n 'ggraph' -- "$@")
|
||||||
|
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||||
|
if [[ $? -ne 0 ]]; then
|
||||||
|
disp E "Invalid options, use \"ggraph --help\" to display usage."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
eval set -- "$PARSED"
|
||||||
|
local limit=30
|
||||||
|
while true; do
|
||||||
|
case "$1" in
|
||||||
|
-h|--help)
|
||||||
|
printf "ggraph: Display decorated git history graph.\n"
|
||||||
|
printf "Usage: ggraph [-n limit]\n"
|
||||||
|
printf "Options:\n"
|
||||||
|
printf "\t-n, --limit\tNumber of commits to display (default: 30)\n"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
-n|--limit)
|
||||||
|
limit="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
disp E "Invalid options, use \"ggraph --help\" to display usage."
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
[[ $limit =~ ^[0-9]+$ ]] || {
|
||||||
|
disp E "Invalid limit: must be a positive integer."
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_git_require_repo || return 1
|
||||||
|
git log --graph --decorate --oneline --all --max-count="$limit"
|
||||||
|
}
|
||||||
|
export -f ggraph
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Sync current branch with remote (fetch + rebase)
|
||||||
|
# Usage: gsync [remote]
|
||||||
|
gsync()
|
||||||
|
{
|
||||||
|
local PARSED
|
||||||
|
PARSED=$(getopt -o h --long help -n 'gsync' -- "$@")
|
||||||
|
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||||
|
if [[ $? -ne 0 ]]; then
|
||||||
|
disp E "Invalid options, use \"gsync --help\" to display usage."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
eval set -- "$PARSED"
|
||||||
|
while true; do
|
||||||
|
case "$1" in
|
||||||
|
-h|--help)
|
||||||
|
printf "gsync: Fetch and rebase current branch onto its remote tracking branch.\n"
|
||||||
|
printf "Usage: gsync [remote]\n"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
disp E "Invalid options, use \"gsync --help\" to display usage."
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
_git_require_repo || return 1
|
||||||
|
|
||||||
|
local remote="${1:-$GIT_DEFAULT_REMOTE}"
|
||||||
|
local branch upstream
|
||||||
|
branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null) || return 1
|
||||||
|
upstream=$(git rev-parse --abbrev-ref --symbolic-full-name '@{u}' 2>/dev/null) || true
|
||||||
|
|
||||||
|
disp I "Fetching from $remote..."
|
||||||
|
git fetch --prune "$remote" || return 1
|
||||||
|
|
||||||
|
if [[ -z $upstream ]]; then
|
||||||
|
disp W "No upstream configured for $branch, skipping rebase."
|
||||||
|
disp I "Set one with: git branch --set-upstream-to ${remote}/${branch} ${branch}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
disp I "Rebasing $branch onto $upstream..."
|
||||||
|
git rebase "$upstream"
|
||||||
|
}
|
||||||
|
export -f gsync
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Add, commit, and push changes with automatic pull/rebase if needed
|
||||||
|
# Usage: gacp -m "message" [file1 file2 ...]
|
||||||
|
gacp()
|
||||||
|
{
|
||||||
|
local PARSED
|
||||||
|
PARSED=$(getopt -o hm: --long help,message: -n 'gacp' -- "$@")
|
||||||
|
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||||
|
if [[ $? -ne 0 ]]; then
|
||||||
|
disp E "Invalid options, use \"gacp --help\" to display usage."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
eval set -- "$PARSED"
|
||||||
|
local msg=""
|
||||||
|
while true; do
|
||||||
|
case "$1" in
|
||||||
|
-h|--help)
|
||||||
|
printf "gacp: Run git add, git commit, and git push in one command.\n"
|
||||||
|
printf "Usage: gacp -m \"message\" [file1 file2 ...]\n"
|
||||||
|
printf "Options:\n"
|
||||||
|
printf "\t-m, --message\tCommit message (mandatory)\n"
|
||||||
|
printf "\n"
|
||||||
|
printf "If files are provided, only those paths are added.\n"
|
||||||
|
printf "If no file is provided, all changes are added with git add -A.\n"
|
||||||
|
printf "If the remote branch moved forward, gacp pulls with rebase before pushing.\n"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
-m|--message)
|
||||||
|
msg="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
disp E "Invalid options, use \"gacp --help\" to display usage."
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
_git_require_repo || return 1
|
||||||
|
|
||||||
|
if [[ -z $msg ]]; then
|
||||||
|
disp E "Missing commit message. Use -m or --message."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local branch upstream remote tracking_branch ahead behind counts
|
||||||
|
branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null) || return 1
|
||||||
|
upstream=$(git rev-parse --abbrev-ref --symbolic-full-name '@{u}' 2>/dev/null) || true
|
||||||
|
|
||||||
|
if [[ $# -gt 0 ]]; then
|
||||||
|
disp I "Adding selected paths..."
|
||||||
|
git add -- "$@" || return 1
|
||||||
|
else
|
||||||
|
disp I "Adding all changes..."
|
||||||
|
git add -A || return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if git diff --cached --quiet; then
|
||||||
|
disp W "No staged changes to commit."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
disp I "Creating commit..."
|
||||||
|
git commit -m "$msg" || return 1
|
||||||
|
|
||||||
|
if [[ -n $upstream ]]; then
|
||||||
|
remote="${upstream%%/*}"
|
||||||
|
tracking_branch="${upstream#*/}"
|
||||||
|
else
|
||||||
|
remote="$GIT_DEFAULT_REMOTE"
|
||||||
|
tracking_branch="$branch"
|
||||||
|
fi
|
||||||
|
|
||||||
|
disp I "Fetching from $remote..."
|
||||||
|
git fetch --prune "$remote" || return 1
|
||||||
|
|
||||||
|
if git rev-parse --verify --quiet "refs/remotes/${remote}/${tracking_branch}" >/dev/null; then
|
||||||
|
counts=$(git rev-list --left-right --count HEAD..."${remote}/${tracking_branch}" 2>/dev/null) || return 1
|
||||||
|
read -r ahead behind <<< "$counts"
|
||||||
|
if [[ ${behind:-0} -gt 0 ]]; then
|
||||||
|
disp I "Remote branch is ahead, rebasing before push..."
|
||||||
|
git pull --rebase "$remote" "$tracking_branch" || return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n $upstream ]]; then
|
||||||
|
disp I "Pushing to $upstream..."
|
||||||
|
git push || return 1
|
||||||
|
else
|
||||||
|
disp I "Pushing and setting upstream to ${remote}/${branch}..."
|
||||||
|
git push -u "$remote" "$branch" || return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
disp I "gacp complete."
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
export -f gacp
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Reset local branch to exact upstream state (stash local changes first)
|
||||||
|
# Usage: greset [target]
|
||||||
|
greset()
|
||||||
|
{
|
||||||
|
local PARSED
|
||||||
|
PARSED=$(getopt -o hx --long help,with-ignored -n 'greset' -- "$@")
|
||||||
|
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||||
|
if [[ $? -ne 0 ]]; then
|
||||||
|
disp E "Invalid options, use \"greset --help\" to display usage."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
eval set -- "$PARSED"
|
||||||
|
local clean_ignored=0
|
||||||
|
while true; do
|
||||||
|
case "$1" in
|
||||||
|
-h|--help)
|
||||||
|
printf "greset: Reset current branch to upstream, stashing local changes first.\n"
|
||||||
|
printf "Usage: greset [target]\n"
|
||||||
|
printf "Options:\n"
|
||||||
|
printf "\t-x, --with-ignored\tAlso remove ignored files (git clean -fdx)\n"
|
||||||
|
printf "\n"
|
||||||
|
printf "Default target is current branch upstream (@{u}).\n"
|
||||||
|
printf "If no upstream exists, fallback target is <remote>/<branch>.\n"
|
||||||
|
printf "This command stashes local modifications (tracked + untracked),\n"
|
||||||
|
printf "drops local unpushed commits by hard-reset, and cleans untracked files.\n"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
-x|--with-ignored)
|
||||||
|
clean_ignored=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
disp E "Invalid options, use \"greset --help\" to display usage."
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
_git_require_repo || return 1
|
||||||
|
|
||||||
|
local branch upstream target remote old_head stash_msg stash_out stash_created=0 dropped=0
|
||||||
|
branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null) || return 1
|
||||||
|
upstream=$(git rev-parse --abbrev-ref --symbolic-full-name '@{u}' 2>/dev/null) || true
|
||||||
|
target="$1"
|
||||||
|
|
||||||
|
if [[ -z $target ]]; then
|
||||||
|
if [[ -n $upstream ]]; then
|
||||||
|
target="$upstream"
|
||||||
|
else
|
||||||
|
remote="$GIT_DEFAULT_REMOTE"
|
||||||
|
target="${remote}/${branch}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z $remote ]]; then
|
||||||
|
remote="${target%%/*}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
old_head=$(git rev-parse HEAD 2>/dev/null) || return 1
|
||||||
|
|
||||||
|
if ! git diff --quiet || ! git diff --cached --quiet || [[ -n $(git ls-files --others --exclude-standard) ]]; then
|
||||||
|
stash_msg="greset:${branch}:$(date +'%Y-%m-%d %H:%M:%S')"
|
||||||
|
disp I "Stashing local changes as '$stash_msg'..."
|
||||||
|
stash_out=$(git stash push -u -m "$stash_msg" 2>&1) || {
|
||||||
|
disp E "Failed to stash local changes."
|
||||||
|
printf "%s\n" "$stash_out"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
[[ $stash_out != "No local changes to save"* ]] && stash_created=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
disp I "Fetching from $remote..."
|
||||||
|
git fetch --prune "$remote" || return 1
|
||||||
|
|
||||||
|
if ! git rev-parse --verify --quiet "$target" >/dev/null; then
|
||||||
|
disp E "Target '$target' does not exist."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
dropped=$(git rev-list --count "${target}..${old_head}" 2>/dev/null || printf "0")
|
||||||
|
|
||||||
|
disp W "Hard-resetting $branch to $target..."
|
||||||
|
git reset --hard "$target" || return 1
|
||||||
|
|
||||||
|
if (( clean_ignored )); then
|
||||||
|
git clean -fdx || return 1
|
||||||
|
else
|
||||||
|
git clean -fd || return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( stash_created )); then
|
||||||
|
disp I "Local changes were stashed. Use 'git stash list' and 'git stash pop' when needed."
|
||||||
|
fi
|
||||||
|
disp I "greset complete. Dropped local-only commits: $dropped"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
export -f greset
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Create a quick WIP commit for local checkpointing
|
||||||
|
# Usage: gwip [message]
|
||||||
|
gwip()
|
||||||
|
{
|
||||||
|
local PARSED
|
||||||
|
PARSED=$(getopt -o h --long help -n 'gwip' -- "$@")
|
||||||
|
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||||
|
if [[ $? -ne 0 ]]; then
|
||||||
|
disp E "Invalid options, use \"gwip --help\" to display usage."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
eval set -- "$PARSED"
|
||||||
|
while true; do
|
||||||
|
case "$1" in
|
||||||
|
-h|--help)
|
||||||
|
printf "gwip: Create a local checkpoint commit with all tracked/untracked changes.\n"
|
||||||
|
printf "Usage: gwip [message]\n"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
disp E "Invalid options, use \"gwip --help\" to display usage."
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
_git_require_repo || return 1
|
||||||
|
|
||||||
|
local msg
|
||||||
|
if [[ $# -gt 0 ]]; then
|
||||||
|
msg="$*"
|
||||||
|
else
|
||||||
|
msg="$GIT_WIP_PREFIX: $(date +'%Y-%m-%d %H:%M:%S')"
|
||||||
|
fi
|
||||||
|
|
||||||
|
git add -A || return 1
|
||||||
|
git commit -m "$msg"
|
||||||
|
}
|
||||||
|
export -f gwip
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Delete merged local branches (except protected branches)
|
||||||
|
# Usage: gprune [main-branch]
|
||||||
|
gprune()
|
||||||
|
{
|
||||||
|
local PARSED
|
||||||
|
PARSED=$(getopt -o h --long help -n 'gprune' -- "$@")
|
||||||
|
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||||
|
if [[ $? -ne 0 ]]; then
|
||||||
|
disp E "Invalid options, use \"gprune --help\" to display usage."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
eval set -- "$PARSED"
|
||||||
|
while true; do
|
||||||
|
case "$1" in
|
||||||
|
-h|--help)
|
||||||
|
printf "gprune: Delete local branches already merged into main branch.\n"
|
||||||
|
printf "Usage: gprune [main-branch]\n"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
disp E "Invalid options, use \"gprune --help\" to display usage."
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
_git_require_repo || return 1
|
||||||
|
|
||||||
|
local base="${1:-$(_git_default_branch "$GIT_DEFAULT_REMOTE")}" current deleted=0
|
||||||
|
current=$(git rev-parse --abbrev-ref HEAD 2>/dev/null) || return 1
|
||||||
|
|
||||||
|
disp I "Pruning branches merged into $base..."
|
||||||
|
|
||||||
|
while IFS= read -r b; do
|
||||||
|
[[ -z $b ]] && continue
|
||||||
|
[[ $b == "$current" ]] && continue
|
||||||
|
[[ $b == "$base" ]] && continue
|
||||||
|
[[ $b == "master" || $b == "main" || $b == "develop" || $b == "dev" ]] && continue
|
||||||
|
git branch -d "$b" >/dev/null 2>&1 && {
|
||||||
|
printf "Deleted: %s\n" "$b"
|
||||||
|
((deleted++))
|
||||||
|
}
|
||||||
|
done < <(git branch --merged "$base" | sed -E 's/^\*?\s*//')
|
||||||
|
|
||||||
|
(( deleted == 0 )) && disp I "No merged branches to delete."
|
||||||
|
}
|
||||||
|
export -f gprune
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Print repository root path
|
||||||
|
# Usage: groot
|
||||||
|
groot()
|
||||||
|
{
|
||||||
|
local PARSED
|
||||||
|
PARSED=$(getopt -o hg --long help,go -n 'groot' -- "$@")
|
||||||
|
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||||
|
if [[ $? -ne 0 ]]; then
|
||||||
|
disp E "Invalid options, use \"groot --help\" to display usage."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
eval set -- "$PARSED"
|
||||||
|
local do_go=0
|
||||||
|
while true; do
|
||||||
|
case "$1" in
|
||||||
|
-h|--help)
|
||||||
|
printf "groot: Display the absolute path of the current repository root.\n"
|
||||||
|
printf "Usage: groot [-g|--go]\n"
|
||||||
|
printf "Options:\n"
|
||||||
|
printf "\t-g, --go\tChange current directory to repository root\n"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
-g|--go)
|
||||||
|
do_go=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
disp E "Invalid options, use \"groot --help\" to display usage."
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
_git_require_repo || return 1
|
||||||
|
local root
|
||||||
|
root=$(git rev-parse --show-toplevel) || return 1
|
||||||
|
|
||||||
|
if (( do_go )); then
|
||||||
|
cd "$root" || {
|
||||||
|
disp E "Failed to move to repository root: $root"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf "%s\n" "$root"
|
||||||
|
}
|
||||||
|
export -f groot
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
load_conf git
|
||||||
|
|
||||||
|
# EOF
|
||||||
@@ -52,7 +52,15 @@ help()
|
|||||||
printf "findbig\t\tFind the biggest files in the given or current directory\n"
|
printf "findbig\t\tFind the biggest files in the given or current directory\n"
|
||||||
printf "finddead\tFind dead symbolic links in the given or current directory\n"
|
printf "finddead\tFind dead symbolic links in the given or current directory\n"
|
||||||
printf "findzero\tFind empty files in the given or current directory\n"
|
printf "findzero\tFind empty files in the given or current directory\n"
|
||||||
|
printf "gacp\t\tAdd, commit and push changes (auto-pull if needed)\n"
|
||||||
printf "genpwd\t\tGenerate one or more random secure passwords with configurable constraints\n"
|
printf "genpwd\t\tGenerate one or more random secure passwords with configurable constraints\n"
|
||||||
|
printf "ggraph\t\tDisplay decorated git history graph\n"
|
||||||
|
printf "gprune\t\tDelete local branches already merged into main branch\n"
|
||||||
|
printf "greset\t\tReset branch to upstream (stash local, drop local commits)\n"
|
||||||
|
printf "groot\t\tDisplay repository root path (or cd to it with -g)\n"
|
||||||
|
printf "gsync\t\tFetch and rebase current branch onto upstream\n"
|
||||||
|
printf "gst\t\tDisplay short git status and branch tracking info\n"
|
||||||
|
printf "gwip\t\tCreate a quick WIP checkpoint commit\n"
|
||||||
printf "gpid\t\tGive the list of PIDs matching the given process name(s)\n"
|
printf "gpid\t\tGive the list of PIDs matching the given process name(s)\n"
|
||||||
printf "isipv4\t\tTell if the given parameter is a valid IPv4 address\n"
|
printf "isipv4\t\tTell if the given parameter is a valid IPv4 address\n"
|
||||||
printf "isipv6\t\tTell if the given parameter is a valid IPv6 address\n"
|
printf "isipv6\t\tTell if the given parameter is a valid IPv6 address\n"
|
||||||
|
|||||||
Reference in New Issue
Block a user