Skip to content

Instantly share code, notes, and snippets.

@KenanBek
Created December 26, 2022 01:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save KenanBek/294d21b95a85cee9c54d0a17a8c90dcb to your computer and use it in GitHub Desktop.
Save KenanBek/294d21b95a85cee9c54d0a17a8c90dcb to your computer and use it in GitHub Desktop.
Shell script to clone repositories at a specific branch, remove the `.git` folder, and have a clean, archived copy of the repository. Read more: https://kenanbek.medium.com/archive-git-repositories-with-shell-script-987d944af769
# parse the command-line arguments
while getopts ":nh" opt; do
case ${opt} in
h)
echo "Usage:"
echo " git-clone.sh [-n]"
echo ""
echo "Options:"
echo " -n Skip commit and push"
exit 0
;;
n)
# set a flag to skip the commit_and_push function
no_push=1
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 1
;;
esac
done
# list of repositories with list of branches for each repository
repos=(
"git@github.com:KenanBek/dbui.git;main,dev"
"git@github.com:KenanBek/django-skeleton.git;master"
)
# define ANSI escape codes for color
COLOR_GREEN='\033[32m'
COLOR_RESET='\033[0m'
# extract rightmost text until the first "/" from the right and remove the rightmost ".git"
function extract_repo_name {
# input string
input=$1
# extract rightmost text until the first "/" from the right
result="${input##*/}"
# remove the rightmost ".git"
result="${result%.git}"
# return the result
echo "$result"
}
# clone a repository by the given repository and branch
function clone_repo {
# repository url, name, and branch
repo_url=$1
repo_name=$(extract_repo_name $repo_url)
branch_name=$2
# construct the folder name
folder_name=$repo_name-$branch_name
# print the cloning message
echo "Cloning ${COLOR_GREEN}\"$repo_name\"${COLOR_RESET} at branch ${COLOR_GREEN}\"$branch_name\"${COLOR_RESET} into folder ${COLOR_GREEN}\"$folder_name\"${COLOR_RESET}"
# if the folder already exists, check the commit hash
if [ -d "$folder_name" ]; then
echo "Folder ${COLOR_GREEN}\"$folder_name\"${COLOR_RESET} already exists"
echo "Checking commit hash"
# get the commit hash from the file
commit_hash=$(cat $folder_name/commit-hash.txt)
# get the commit hash from the remote
remote_commit_hash=$(git ls-remote $repo_url $branch_name | cut -f 1)
# if the commit hashes are equal, skip the repository
if [ "$commit_hash" == "$remote_commit_hash" ]; then
echo "Commit hash ${COLOR_GREEN}\"$commit_hash\"${COLOR_RESET} is equal to remote commit hash ${COLOR_GREEN}\"$remote_commit_hash\"${COLOR_RESET}, skipping"
return
fi
# otherwise, remove the folder
echo "Commit hash ${COLOR_GREEN}\"$commit_hash\"${COLOR_RESET} is not equal to remote commit hash ${COLOR_GREEN}\"$remote_commit_hash\"${COLOR_RESET}, removing folder ${COLOR_GREEN}\"$folder_name\"${COLOR_RESET}"
rm -rf $folder_name
fi
# clone the repository at the branch into the folder
git clone -b $branch_name $repo_url $folder_name
# write commit hash to file
git ls-remote $repo_url $branch_name | cut -f 1 > $folder_name/commit-hash.txt
# remove the .git folder
rm -rf $folder_name/.git
}
# function to commit changes in the current folder and push them to the remote
function commit_and_push {
# commit the changes
git add .
git commit -m "Update at $(date)"
# push the changes
git push
}
# iterate over the list and clone each repository
for repo in "${repos[@]}"
do
# split the repository name and branch names
repo_url=$(echo $repo | cut -d ';' -f 1)
branch_names=$(echo $repo | cut -d ';' -f 2)
# iterate over the branch names and clone the repository for each branch
for branch_name in $(echo $branch_names | tr "," "\n")
do
clone_repo $repo_url $branch_name
echo ""
done
done
# iterate over all subfolders and verify .git folder is removed
for folder in ./*
do
# if the folder is not a directory, skip it
if [ ! -d "$folder" ]; then
continue
fi
# if the folder contains a .git folder, remove it
if [ -d "$folder/.git" ]; then
echo "Removing .git folder from ${COLOR_GREEN}\"$folder\"${COLOR_RESET}"
rm -rf $folder/.git
fi
done
# wait for all sub-processes to finish
wait
# commit and push the changes
# check the no_push flag before calling the commit_and_push function
if [ -z "$no_push" ]; then
# commit and push the changes
echo "Committing and pushing changes"
commit_and_push
else
# print the skipping message
echo "Skipping commit and push"
fi
echo "${COLOR_GREEN}Done${COLOR_RESET}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment