I'm a big fan of git worktree
command. My project directory contains a number of folders which points to branches which I'm working on at the moment.
.../project directory
├── bugfix
│ ├── IBD-15
│ ├── IBD-17
│ ├── IBD-18
│ ├── IBD-21
│ ├── IBD-22
│ └── IBD-23
...
├── main
└── story
├── logs
└── IBD-7
X directories
Over a time I close a feature via merge request using CI instruments (CI automatically deletes a remote branch). But feature branch keeps alive in the project folder and I need periodically watch through the list of closed features and manually remove the folders which points to non-existent branches on remote:
$ rm -rf bugfix/IBD-15
$ rm -rf bugfix/IBD-23
...
$ git worktree prune
So is there a way to get rid worktree folders for branches which don't exist anymore on remote (of course if the 'worktree' folder doesn't contain not pushed commits or untracked local changes) ?
CodePudding user response:
I just tested what happens after a git fetch --prune
regarding existing worktree.
A git branch -avv
would show:
ee 60f5e46 (C:/Users/vonc/git/tests/b2/ee) [origin/ee: gone] Move...
That means you can do something like:
git branch -avv|grep ": gone"|cut -f 2 -d '('|cut -f 1 -d ')'|xargs git worktree remove
And all your obsolete worktrees would be gone. In one line.
CodePudding user response:
I wrote for weekends an interactive bash script which walk through all worktree folders and ask user for removing the folder. The script checks if upstream branch exists on remote or unpunished commits exist in worktree folder. You need to run the script from any worktree folder and it will do it's job.
#!/usr/bin/env bash
# filename: worktree-cleanup
#
# get rid 'worktree' folders which branches doesn't exists
# This script automatically walks through all wortree folders
# and ask for removing them
#
CUR_DIR=$(pwd)
function pushd {
command pushd "$@" > /dev/null
}
function popd {
command popd "$@" > /dev/null
}
function locate_git_tool {
for path in ${PATH//:/ }; do
[ -x "$path/git" ] && echo "$path/git" && break
done
echo ""
} # end of function locate_git_tool
function git_fetch {
$GIT_TOOL fetch --all
}
function contains_untracked_changes {
local dir=$1
pushd "$dir"
local status="$($GIT_TOOL status --short)"
popd
echo "$status"
} # end of function contains_untracked_changes
function is_git_dir {
local dir="$1"
pushd "$dir"
[ -d "$dir" ] && [ -d "$dir/.git" -o -e "$dir/.git" ]
popd
} #end of function is_git_dir
function is_dir_exist {
[ -e "$@" -a -d "$@" ]
} # end of function is_dir_exist
function is_current_dir {
[ "$CUR_DIR" == "$@" ]
}
function choose {
local default="$1"
local prompt="$2"
local choice_yes="$3"
local choice_no="$4"
local answer
read -p "$prompt" answer
[ -z "$answer" ] && answer="$default"
case "$answer" in
[yY1] ) eval "$choice_yes"
;;
[nN0] ) eval "$choice_no"
;;
* ) printf "%b" "Unexpected answer '$answer'!" >&2 ;;
esac
} # end of function choose
function contains_unpushed_commits {
pushd "$@"
local output=$(git log @{u}.. -p)
popd
echo "$output"
}
function is_upstream_branch_exists {
local path="$1"
local branch="$2"
pushd "$path"
local output=$(git ls-remote --heads --quiet | grep 'branch')
popd
echo "$output"
}
function cleanup_worktree_folder {
local path="$1"
choose "y" \
"Remove $path? (y or n)" \
"$GIT_TOOL worktree remove --force \"$path\"" \
":"
}
function check_and_cleanup_forlder {
local path="$1"
local branch="$2"
if [ ! -z "$(contains_untracked_changes $path)" ] ; then
printf "%s\n%s\n" "Warning: $path contains changes" "$(contains_untracked_changes $path)"
cleanup_worktree_folder "$path"
elif [ -z "$(is_upstream_branch_exists $path $branch)" ] ; then
printf "%s\n" "Warning: $path doesn't have upstream branch"
cleanup_worktree_folder "$path"
elif [ ! -z "$(contains_unpushed_commits $path)" ] ; then
printf "%s\n%s\n" "Warning: $path" "$(contains_unpushed_commits $path)"
else
cleanup_worktree_folder "$path"
fi
}
GIT_TOOL=$(locate_git_tool)
[ -x "${GIT_TOOL}" ] || { printf "%b" "Fatal: 'git' tool not found.\n" ; exit 1 ; }
[ -d "$CUR_DIR/.git" -o -e "$CUR_DIR/.git" ] || { printf "%b" "Fatal: not a git repository.\n" ; exit 1 ; }
git_fetch
IFS=$'\n'
for line in $($GIT_TOOL worktree list) ; do
path=$(echo $line | awk '{print $1}')
branch=$(echo $line | awk '{print $3}')
branch="${branch%]}"
branch="${branch#[}"
if ! $(is_dir_exist "$path") ; then
echo "$path not exists"
elif $(is_current_dir "$path") ; then
:
elif ! $(is_git_dir "$path") ; then
printf "%b" "Warning: not a git repository. Skip.\n"
else
check_and_cleanup_forlder "$path" "$branch"
fi
done
unset IFS
You can find the script here