mirror of
https://github.com/wfxr/forgit.git
synced 2026-09-10 07:16:23 -04:00
feat: add interactive git worktree operations
Add worktree browser (gwt) and worktree delete selector (gwd) with: - Interactive worktree list with lock status, branch info, and age - Preview showing working tree status and recent commits - Ctrl-Y to copy worktree path, Alt-L to toggle lock/unlock - Shell integration for zsh (with cd) and fish (with cd) - Tab completions for zsh, bash, and fish - Documentation in README with keybindings and options Co-Authored-By: Sufien Tout <sufientout@gmail.com>
This commit is contained in:
parent
ea58599e66
commit
fc081846c0
14
README.md
14
README.md
|
|
@ -69,6 +69,8 @@ It's **lightweight** and **easy to use**.
|
|||
| `gsq` | Interactive `git commit --squash && git rebase -i --autosquash` selector |
|
||||
| `grw` | Interactive `git commit --fixup=reword && git rebase -i --autosquash` selector |
|
||||
| `gclean` | Interactive `git clean` selector |
|
||||
| `gwt` | Interactive `git worktree` selector |
|
||||
| `gwd` | Interactive `git worktree remove` selector |
|
||||
|
||||
# 📥 Installation
|
||||
|
||||
|
|
@ -197,6 +199,8 @@ forgit_blame=gbl
|
|||
forgit_fixup=gfu
|
||||
forgit_squash=gsq
|
||||
forgit_reword=grw
|
||||
forgit_worktree=gwt
|
||||
forgit_worktree_delete=gwd
|
||||
```
|
||||
|
||||
## Git Integration
|
||||
|
|
@ -271,6 +275,8 @@ Each forgit command can be customized with dedicated environment variables for g
|
|||
| `gfu` | `FORGIT_FIXUP_GIT_OPTS` | `FORGIT_FIXUP_FZF_OPTS` |
|
||||
| `gsq` | `FORGIT_SQUASH_GIT_OPTS` | `FORGIT_SQUASH_FZF_OPTS` |
|
||||
| `grw` | `FORGIT_REWORD_GIT_OPTS` | `FORGIT_REWORD_FZF_OPTS` |
|
||||
| `gwt` | | `FORGIT_WORKTREE_FZF_OPTS` |
|
||||
| `gwd` | `FORGIT_WORKTREE_DELETE_GIT_OPTS` | `FORGIT_WORKTREE_DELETE_FZF_OPTS` |
|
||||
|
||||
## Pagers
|
||||
|
||||
|
|
@ -352,15 +358,19 @@ export FORGIT_LOG_FZF_OPTS='
|
|||
| <kbd>Alt</kbd> - <kbd>W</kbd> | Toggle preview wrap |
|
||||
| <kbd>Ctrl</kbd> - <kbd>S</kbd> | Toggle sort |
|
||||
| <kbd>Ctrl</kbd> - <kbd>R</kbd> | Toggle selection |
|
||||
| <kbd>Ctrl</kbd> - <kbd>Y</kbd> | Copy commit hash/stash ID* |
|
||||
| <kbd>Ctrl</kbd> - <kbd>Y</kbd> | Copy commit hash/stash ID/worktree path<sup>1</sup> |
|
||||
| <kbd>Ctrl</kbd> - <kbd>K</kbd> / <kbd>P</kbd> | Selection move up |
|
||||
| <kbd>Ctrl</kbd> - <kbd>J</kbd> / <kbd>N</kbd> | Selection move down |
|
||||
| <kbd>Alt</kbd> - <kbd>K</kbd> / <kbd>P</kbd> | Preview move up |
|
||||
| <kbd>Alt</kbd> - <kbd>J</kbd> / <kbd>N</kbd> | Preview move down |
|
||||
| <kbd>Alt</kbd> - <kbd>E</kbd> | Open file in default editor (when possible) |
|
||||
| <kbd>Alt</kbd> - <kbd>T</kbd> | Show commit message (when viewing a commit) |
|
||||
| <kbd>Alt</kbd> - <kbd>L</kbd> | Toggle worktree lock/unlock<sup>2</sup> |
|
||||
|
||||
<sup>1</sup> Available when the selection contains a commit hash, stash ID, or worktree path.
|
||||
|
||||
<sup>2</sup> Available in the worktree browser (`gwt`) and worktree delete selector (`gwd`).
|
||||
|
||||
\* Available when the selection contains a commit hash or a stash ID.
|
||||
For Linux users `FORGIT_COPY_CMD` should be set to make copy work. Example: `FORGIT_COPY_CMD='xclip -selection clipboard'`.
|
||||
|
||||
# 📦 Optional dependencies
|
||||
|
|
|
|||
148
bin/git-forgit
148
bin/git-forgit
|
|
@ -32,6 +32,8 @@ $FORGIT_FZF_DEFAULT_OPTS
|
|||
_forgit_warn() { printf "%b[Warn]%b %s\n" '\e[0;33m' '\e[0m' "$@" >&2; }
|
||||
_forgit_info() { printf "%b[Info]%b %s\n" '\e[0;32m' '\e[0m' "$@" >&2; }
|
||||
_forgit_inside_work_tree() { git rev-parse --is-inside-work-tree >/dev/null 2>&1; }
|
||||
_forgit_inside_git_dir() { git rev-parse --is-inside-git-dir >/dev/null 2>&1; }
|
||||
_forgit_inside_git_repo() { _forgit_inside_work_tree || _forgit_inside_git_dir; }
|
||||
# tac is not available on OSX, tail -r is not available on Linux, so we use either of them
|
||||
_forgit_reverse_lines() { tac 2> /dev/null || tail -r; }
|
||||
_forgit_strip_ansi() { local ESC=$'\033'; sed "s/${ESC}\[[0-9;]*m//g"; }
|
||||
|
|
@ -1259,6 +1261,146 @@ _forgit_paths_list() {
|
|||
find "$path" -name "*$ext" -print |sed -e "s#$ext\$##" -e 's#.*/##' -e '/^$/d' | sort -fu
|
||||
}
|
||||
|
||||
# Parse git worktree list --porcelain output and format it for display
|
||||
# Output format: [XY] /path/to/worktree (branch) 3 hours ago
|
||||
# X: '*' = current worktree, ' ' = other
|
||||
# Y: 'L' (yellow) = locked, 'P' (yellow) = prunable, ' ' = normal
|
||||
# When both locked and prunable, 'L' takes precedence
|
||||
_forgit_worktree_list() {
|
||||
local worktree head branch locked prunable line relative_date
|
||||
local _cyan=$'\033[36m' _gray=$'\033[90m' _yellow=$'\033[33m' _reset=$'\033[0m'
|
||||
local current_worktree
|
||||
current_worktree=$(git rev-parse --show-toplevel 2>/dev/null)
|
||||
git worktree list --porcelain | while IFS= read -r line; do
|
||||
case "$line" in
|
||||
"worktree "*)
|
||||
worktree="${line#worktree }"
|
||||
head="" branch="" locked="" prunable=""
|
||||
;;
|
||||
"HEAD "*)
|
||||
head="${line#HEAD }"
|
||||
;;
|
||||
"branch "*)
|
||||
branch="${line#branch refs/heads/}"
|
||||
;;
|
||||
"detached")
|
||||
branch="detached"
|
||||
;;
|
||||
"locked"*)
|
||||
locked="${_yellow}L${_reset}"
|
||||
;;
|
||||
"prunable"*)
|
||||
prunable="${_yellow}P${_reset}"
|
||||
;;
|
||||
"")
|
||||
relative_date=$(git log -1 --format='%cr' "$head" 2>/dev/null)
|
||||
local current_marker=" " lock_marker=" "
|
||||
[[ "$worktree" == "$current_worktree" ]] && current_marker="*"
|
||||
[[ -n "$prunable" ]] && lock_marker="$prunable"
|
||||
[[ -n "$locked" ]] && lock_marker="$locked"
|
||||
printf "[%s%s] %s ${_cyan}(%s)${_reset} ${_gray}%s${_reset}\n" \
|
||||
"$current_marker" "$lock_marker" "$worktree" "${branch:-HEAD}" "$relative_date"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
# Return deletable worktrees (exclude main worktree which is the first one)
|
||||
_forgit_worktree_list_deletable() {
|
||||
_forgit_worktree_list | tail -n +2
|
||||
}
|
||||
|
||||
# Extract worktree path from formatted line (strip ANSI codes, skip 5-char prefix '[XY] ')
|
||||
# TODO: awk '{print $1}' breaks on paths containing spaces or parentheses.
|
||||
# Consider switching _forgit_worktree_list to a tab-delimited format so we can
|
||||
# use 'cut -f1' (or awk -F'\t') for reliable path extraction.
|
||||
_forgit_extract_worktree_path() {
|
||||
_forgit_strip_ansi | cut -c6- | awk '{print $1}'
|
||||
}
|
||||
|
||||
# Copy worktree path to clipboard
|
||||
_forgit_worktree_yank_path() {
|
||||
echo "$1" | _forgit_extract_worktree_path | ${FORGIT_COPY_CMD:-pbcopy}
|
||||
}
|
||||
|
||||
# Toggle worktree lock status (check 3rd char 'L' in prefix '[XY]')
|
||||
_forgit_worktree_toggle_lock() {
|
||||
local line="$1" worktree stripped
|
||||
worktree=$(echo "$line" | _forgit_extract_worktree_path)
|
||||
stripped=$(echo "$line" | _forgit_strip_ansi)
|
||||
if [[ "${stripped:2:1}" == "L" ]]; then
|
||||
git worktree unlock "$worktree"
|
||||
else
|
||||
git worktree lock "$worktree"
|
||||
fi
|
||||
}
|
||||
|
||||
# Preview function for worktree
|
||||
_forgit_worktree_preview() {
|
||||
local worktree
|
||||
worktree=$(echo "$1" | _forgit_extract_worktree_path)
|
||||
[[ ! -d "$worktree" ]] && echo "Worktree directory not found: $worktree" && return 1
|
||||
|
||||
local status_output
|
||||
status_output=$(git -c color.status=always -C "$worktree" status -s 2>/dev/null)
|
||||
[[ -n "$status_output" ]] && echo "$status_output" && echo ""
|
||||
git -C "$worktree" log --oneline -n 200 --color=always 2>/dev/null
|
||||
}
|
||||
|
||||
# Git worktree delete wrapper
|
||||
_forgit_git_worktree_delete() {
|
||||
_forgit_worktree_delete_git_opts=()
|
||||
_forgit_parse_array _forgit_worktree_delete_git_opts "$FORGIT_WORKTREE_DELETE_GIT_OPTS"
|
||||
git worktree remove "${_forgit_worktree_delete_git_opts[@]}" "$@"
|
||||
}
|
||||
|
||||
# git worktree browser
|
||||
# Note: we intentionally do NOT use --header-lines=1 here, because the current
|
||||
# worktree (listed first) should remain selectable for operations like lock/unlock.
|
||||
_forgit_worktree() {
|
||||
_forgit_inside_git_repo || return 1
|
||||
local opts worktree
|
||||
[[ $# -ne 0 ]] && { git worktree "$@"; return $?; }
|
||||
|
||||
opts="
|
||||
$FORGIT_FZF_DEFAULT_OPTS
|
||||
+s +m --tiebreak=index
|
||||
--preview=\"$FORGIT worktree_preview {}\"
|
||||
--bind=\"ctrl-y:execute-silent($FORGIT worktree_yank_path {})\"
|
||||
--bind=\"alt-l:execute-silent($FORGIT worktree_toggle_lock {})+reload($FORGIT worktree_list)\"
|
||||
$FORGIT_WORKTREE_FZF_OPTS
|
||||
"
|
||||
worktree=$(_forgit_worktree_list | FZF_DEFAULT_OPTS="$opts" fzf)
|
||||
[[ -z "$worktree" ]] && return 1
|
||||
echo "$worktree" | _forgit_extract_worktree_path
|
||||
}
|
||||
|
||||
# git worktree delete selector
|
||||
_forgit_worktree_delete() {
|
||||
_forgit_inside_git_repo || return 1
|
||||
local opts worktrees
|
||||
[[ $# -ne 0 ]] && { _forgit_git_worktree_delete "$@"; return $?; }
|
||||
|
||||
opts="
|
||||
$FORGIT_FZF_DEFAULT_OPTS
|
||||
+s --multi --tiebreak=index
|
||||
--preview=\"$FORGIT worktree_preview {}\"
|
||||
--bind=\"ctrl-y:execute-silent($FORGIT worktree_yank_path {})\"
|
||||
$FORGIT_WORKTREE_DELETE_FZF_OPTS
|
||||
"
|
||||
|
||||
worktrees=()
|
||||
while IFS='' read -r line; do
|
||||
[[ -n "$line" ]] && worktrees+=("$(echo "$line" | _forgit_extract_worktree_path)")
|
||||
done < <(_forgit_worktree_list_deletable | FZF_DEFAULT_OPTS="$opts" fzf)
|
||||
|
||||
[[ ${#worktrees[@]} -eq 0 ]] && return 1
|
||||
|
||||
for worktree in "${worktrees[@]}"; do
|
||||
_forgit_git_worktree_delete "$worktree"
|
||||
done
|
||||
}
|
||||
|
||||
check_prequisites() {
|
||||
local installed_fzf_version
|
||||
local higher_fzf_version
|
||||
|
|
@ -1332,6 +1474,8 @@ PUBLIC_COMMANDS=(
|
|||
"show"
|
||||
"stash_show"
|
||||
"stash_push"
|
||||
"worktree"
|
||||
"worktree_delete"
|
||||
)
|
||||
|
||||
PRIVATE_COMMANDS=(
|
||||
|
|
@ -1363,6 +1507,10 @@ PRIVATE_COMMANDS=(
|
|||
"edit_diffed_file"
|
||||
"edit_add_file"
|
||||
"pager"
|
||||
"worktree_preview"
|
||||
"worktree_yank_path"
|
||||
"worktree_toggle_lock"
|
||||
"worktree_list"
|
||||
)
|
||||
|
||||
# Check if the script is being sourced. This is necessary for unit tests where
|
||||
|
|
|
|||
|
|
@ -18,6 +18,10 @@ _git-stash-show() {
|
|||
_alternative "files:filename:($(git stash list | sed -n -e 's/:.*//p'))"
|
||||
}
|
||||
|
||||
_git-worktrees() {
|
||||
_alternative "worktrees:worktree:($(git worktree list --porcelain 2>/dev/null | grep '^worktree ' | cut -d' ' -f2-))"
|
||||
}
|
||||
|
||||
# The completions for git already define a _git-diff completion function, but
|
||||
# it provides the wrong results when called from _git-forgit because it heavily
|
||||
# depends on the context it's been called from (usage of $curcontext and
|
||||
|
|
@ -79,6 +83,8 @@ _git-forgit() {
|
|||
'squash:git squash'
|
||||
'stash_show:git stash viewer'
|
||||
'stash_push:git stash push selector'
|
||||
'worktree:git worktree browser'
|
||||
'worktree_delete:git worktree remove selector'
|
||||
)
|
||||
_describe -t commands 'git forgit' subcommands
|
||||
;;
|
||||
|
|
@ -102,6 +108,8 @@ _git-forgit() {
|
|||
squash) _git-log ;;
|
||||
stash_show) _git-stash-show ;;
|
||||
show) _git-show ;;
|
||||
worktree) ;;
|
||||
worktree_delete) _git-worktrees ;;
|
||||
esac
|
||||
}
|
||||
|
||||
|
|
@ -131,6 +139,7 @@ compdef _git-log forgit::reword
|
|||
compdef _git-log forgit::squash
|
||||
compdef _git-stash-show forgit::stash::show
|
||||
compdef _git-show forgit::show
|
||||
compdef _git-worktrees forgit::worktree::delete
|
||||
|
||||
# this is the case of calling the command and pressing tab
|
||||
# the very first time of a shell session, we have to manually
|
||||
|
|
|
|||
|
|
@ -35,6 +35,11 @@ _git_stash_show()
|
|||
__gitcomp_nl "$(__git stash list | sed -n -e 's/:.*//p')"
|
||||
}
|
||||
|
||||
_git_worktrees()
|
||||
{
|
||||
__gitcomp_nl "$(__git worktree list --porcelain 2>/dev/null | grep '^worktree ' | cut -d' ' -f2-)"
|
||||
}
|
||||
|
||||
# Completion for git-forgit
|
||||
# This includes git aliases, e.g. "alias.cb=forgit checkout_branch" will
|
||||
# correctly complete available branches on "git cb".
|
||||
|
|
@ -80,6 +85,8 @@ _git_forgit()
|
|||
squash
|
||||
stash_show
|
||||
stash_push
|
||||
worktree
|
||||
worktree_delete
|
||||
"
|
||||
|
||||
case ${cword} in
|
||||
|
|
@ -108,6 +115,7 @@ _git_forgit()
|
|||
show) _git_show ;;
|
||||
squash) _git_log ;;
|
||||
stash_show) _git_stash_show ;;
|
||||
worktree_delete) _git_worktrees ;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
|
|
@ -146,6 +154,7 @@ then
|
|||
__git_complete forgit::show _git_show
|
||||
__git_complete forgit::squash _git_log
|
||||
__git_complete forgit::stash::show _git_stash_show
|
||||
__git_complete forgit::worktree::delete _git_worktrees
|
||||
|
||||
# Completion for forgit plugin shell aliases
|
||||
if [[ -z "$FORGIT_NO_ALIASES" ]]; then
|
||||
|
|
@ -169,5 +178,6 @@ then
|
|||
__git_complete "${forgit_show}" _git_show
|
||||
__git_complete "${forgit_squash}" _git_log
|
||||
__git_complete "${forgit_stash_show}" _git_stash_show
|
||||
__git_complete "${forgit_worktree_delete}" _git_worktrees
|
||||
fi
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
function __fish_forgit_needs_subcommand
|
||||
for subcmd in add blame branch_delete checkout_branch checkout_commit checkout_file checkout_tag \
|
||||
cherry_pick cherry_pick_from_branch clean diff fixup ignore log reflog rebase reset_head \
|
||||
revert_commit reword squash stash_show stash_push switch_branch
|
||||
revert_commit reword squash stash_show stash_push switch_branch worktree worktree_delete
|
||||
if contains -- $subcmd (commandline -opc)
|
||||
return 1
|
||||
end
|
||||
|
|
@ -16,6 +16,10 @@ function __fish_forgit_needs_subcommand
|
|||
return 0
|
||||
end
|
||||
|
||||
function __fish_forgit_worktrees
|
||||
git worktree list --porcelain 2>/dev/null | string match -r '^worktree .+' | string replace 'worktree ' ''
|
||||
end
|
||||
|
||||
# Load helper functions in git completion file
|
||||
not functions -q __fish_git && source $__fish_data_dir/completions/git.fish
|
||||
|
||||
|
|
@ -46,6 +50,8 @@ complete -c git-forgit -n __fish_forgit_needs_subcommand -a squash -d 'git squas
|
|||
complete -c git-forgit -n __fish_forgit_needs_subcommand -a stash_show -d 'git stash viewer'
|
||||
complete -c git-forgit -n __fish_forgit_needs_subcommand -a stash_push -d 'git stash push selector'
|
||||
complete -c git-forgit -n __fish_forgit_needs_subcommand -a switch_branch -d 'git switch branch selector'
|
||||
complete -c git-forgit -n __fish_forgit_needs_subcommand -a worktree -d 'git worktree browser'
|
||||
complete -c git-forgit -n __fish_forgit_needs_subcommand -a worktree_delete -d 'git worktree remove selector'
|
||||
|
||||
complete -c git-forgit -n '__fish_seen_subcommand_from add' -a "(complete -C 'git add ')"
|
||||
complete -c git-forgit -n '__fish_seen_subcommand_from branch_delete' -a "(__fish_git_local_branches)"
|
||||
|
|
@ -68,3 +74,4 @@ complete -c git-forgit -n '__fish_seen_subcommand_from squash' -a "(complete -C
|
|||
complete -c git-forgit -n '__fish_seen_subcommand_from stash_show' -a "(__fish_git_complete_stashes)"
|
||||
complete -c git-forgit -n '__fish_seen_subcommand_from stash_push' -a "(__fish_git_files modified deleted modified-staged-deleted)"
|
||||
complete -c git-forgit -n '__fish_seen_subcommand_from switch_branch' -a "(complete -C 'git switch ')"
|
||||
complete -c git-forgit -n '__fish_seen_subcommand_from worktree_delete' -a "(__fish_forgit_worktrees)"
|
||||
|
|
|
|||
|
|
@ -30,6 +30,15 @@ end
|
|||
# alias `git-forgit` to the full-path of the command
|
||||
alias git-forgit "$FORGIT"
|
||||
|
||||
function forgit::worktree
|
||||
if test (count $argv) -ne 0
|
||||
git-forgit worktree $argv
|
||||
return $status
|
||||
end
|
||||
set -l tree (git-forgit worktree)
|
||||
test -n "$tree"; and cd "$tree"
|
||||
end
|
||||
|
||||
# register abbreviations
|
||||
if test -z "$FORGIT_NO_ALIASES"
|
||||
abbr -a -- (string collect $forgit_add; or string collect "ga") git-forgit add
|
||||
|
|
@ -56,4 +65,6 @@ if test -z "$FORGIT_NO_ALIASES"
|
|||
abbr -a -- (string collect $forgit_revert_commit; or string collect "grc") git-forgit revert_commit
|
||||
abbr -a -- (string collect $forgit_blame; or string collect "gbl") git-forgit blame
|
||||
abbr -a -- (string collect $forgit_checkout_tag; or string collect "gct") git-forgit checkout_tag
|
||||
abbr -a -- (string collect $forgit_worktree; or string collect "gwt") forgit::worktree
|
||||
abbr -a -- (string collect $forgit_worktree_delete; or string collect "gwd") git-forgit worktree_delete
|
||||
end
|
||||
|
|
|
|||
|
|
@ -160,6 +160,17 @@ forgit::attributes() {
|
|||
"$FORGIT" attributes "$@"
|
||||
}
|
||||
|
||||
forgit::worktree() {
|
||||
if [[ $# -ne 0 ]]; then "$FORGIT" worktree "$@"; return $?; fi
|
||||
local tree
|
||||
tree=$("$FORGIT" worktree) || return $?
|
||||
[[ -n "$tree" ]] && builtin cd "$tree" || return 1
|
||||
}
|
||||
|
||||
forgit::worktree::delete() {
|
||||
"$FORGIT" worktree_delete "$@"
|
||||
}
|
||||
|
||||
# register aliases
|
||||
# shellcheck disable=SC2139
|
||||
if [[ -z "$FORGIT_NO_ALIASES" ]]; then
|
||||
|
|
@ -188,6 +199,8 @@ if [[ -z "$FORGIT_NO_ALIASES" ]]; then
|
|||
builtin export forgit_squash="${forgit_squash:-gsq}"
|
||||
builtin export forgit_reword="${forgit_reword:-grw}"
|
||||
builtin export forgit_blame="${forgit_blame:-gbl}"
|
||||
builtin export forgit_worktree="${forgit_worktree:-gwt}"
|
||||
builtin export forgit_worktree_delete="${forgit_worktree_delete:-gwd}"
|
||||
|
||||
builtin alias "${forgit_add}"='forgit::add'
|
||||
builtin alias "${forgit_reset_head}"='forgit::reset::head'
|
||||
|
|
@ -213,5 +226,7 @@ if [[ -z "$FORGIT_NO_ALIASES" ]]; then
|
|||
builtin alias "${forgit_squash}"='forgit::squash'
|
||||
builtin alias "${forgit_reword}"='forgit::reword'
|
||||
builtin alias "${forgit_blame}"='forgit::blame'
|
||||
builtin alias "${forgit_worktree}"='forgit::worktree'
|
||||
builtin alias "${forgit_worktree_delete}"='forgit::worktree::delete'
|
||||
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -39,3 +39,19 @@ function provider_strip_ansi() {
|
|||
bashunit::data_set '\e[1;31mbar\e[0m' "bar"
|
||||
bashunit::data_set 'no-ansi' "no-ansi"
|
||||
}
|
||||
|
||||
# --- _forgit_extract_worktree_path ---
|
||||
|
||||
# @data_provider provider_extract_worktree_path
|
||||
function test_forgit_extract_worktree_path() {
|
||||
local -r input="$1"
|
||||
local -r expected="$2"
|
||||
local actual
|
||||
actual=$(echo "$input" | _forgit_extract_worktree_path)
|
||||
assert_same "$expected" "$actual"
|
||||
}
|
||||
|
||||
function provider_extract_worktree_path() {
|
||||
bashunit::data_set '[* ] /tmp/foo (main) 3 hours ago' "/tmp/foo"
|
||||
bashunit::data_set '[ ] /tmp/bar (feature) 1 day ago' "/tmp/bar"
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue