Skip to content

Commit fd4459c

Browse files
committed
Add rules for bash completion and prepare for cli options.
1 parent 75b1418 commit fd4459c

File tree

5 files changed

+173
-55
lines changed

5 files changed

+173
-55
lines changed

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,16 @@ This script clones a subdirectory of a github/gitlab repository.
88
Test the script with the example config file.
99
By the end of the execution, you will see a `tmp` directory containing the subfolder of the example repository.
1010
```zsh
11-
./git-partial-clone.sh example.conf
11+
./git-partial-clone.sh --file example.conf
1212
```
1313
## Install
14-
Add the script to your `PATH`
14+
Install the script and autocompletion rules.
1515
```zsh
16-
ln -srf git-partial-clone.sh ~/.local/bin/git-partial-clone
16+
./install.sh
1717
```
18-
Then you can execute the script from any directory with your custom config file.
18+
Then you can execute the script from any directory with your custom config file and use `TAB` to autocomplete the CLI options.
1919
```zsh
20-
git-partial-clone path/to/your/config/file.conf
20+
git-partial-clone --file path/to/your/config/file.conf
2121
```
2222

2323
# Configuration

completion-rules.sh

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#
2+
# Bash completion file for the git-partial-clone script
3+
#
4+
# Copyright (c) 2021 Lucero Alvarado
5+
# https://github.com/lu0/git-partial-clone
6+
#
7+
8+
have git-partial-clone &&
9+
_git-partial-clone()
10+
{
11+
local cur prev
12+
local words cword
13+
_init_completion || return
14+
15+
local i use_config_file
16+
for ((i = cword - 1; i > 0; i--)); do
17+
[[ ${words[i]} == --file ]] \
18+
&& use_config_file=true && break
19+
done
20+
21+
case $prev in
22+
--file | -!(-*)f)
23+
# Suggest files with .conf extension
24+
_filedir '?()conf'
25+
return
26+
;;
27+
esac
28+
29+
# Suggest options contained in the 'usage' section of the script
30+
[[ ! $use_config_file ]] \
31+
&& COMPREPLY=($(compgen -W '$(_parse_help "$1")' -- "$cur"))
32+
}
33+
complete -F _git-partial-clone git-partial-clone
34+

git-partial-clone.sh

Lines changed: 74 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -8,82 +8,106 @@
88
# https://github.com/lu0/git-partial-clone
99
#
1010

11-
CONFIG_FILE_PATH=${1}
1211

13-
git-partial-clone() {
12+
main() {
13+
usage() {
14+
# echo "This script must be run with super-user privileges."
15+
# echo -e "\nUsage: \$0 [arguments] \n"
16+
echo -e "\nClone a subdirectory of a github/gitlab repository."
17+
echo -e "\nUSAGE:"
18+
echo -e " git-partial-clone [OPTIONS] ARGUMENTS"
19+
echo -e "\nOPTIONS:"
20+
echo -e " Using a config file:"
21+
echo -e " -f | --file Path to the configuration file.\n"
22+
echo
23+
}
24+
case $# in
25+
2)
26+
FILE_PATH=${2}
27+
[ ${FILE_PATH} ] \
28+
&& _git-partial-clone ${FILE_PATH} \
29+
|| usage
30+
;;
31+
*)
32+
usage
33+
;;
34+
esac
35+
}
36+
37+
_git-partial-clone() {
1438
# Source config file
15-
[ ${1} ] \
16-
&& get-variables-from-file ${1} \
17-
|| { notif err "git-partial-clone requires a configuration file." && abort ;}
39+
[ -f ${1} ] \
40+
&& _get-variables-from-file ${1} \
41+
|| _notif err "Not a valid path."
1842

19-
check-mandatory-vars "GIT_HOST REPO_NAME REPO_OWNER" || abort
20-
get-token-from-file "${TOKEN_PATH}" GIT_TOKEN
43+
_check-mandatory-vars "GIT_HOST REPO_NAME REPO_OWNER" || _abort
44+
_get-token-from-file "${TOKEN_PATH}" GIT_TOKEN
2145

2246
# Change working directory
23-
get-clone-dir-path "${PARENT_DIR}" "${REPO_NAME}" CLONE_DIR || abort
24-
mkdir "${CLONE_DIR}" && cd "${CLONE_DIR}" || abort
47+
_get-clone-dir-path "${PARENT_DIR}" "${REPO_NAME}" CLONE_DIR || _abort
48+
mkdir "${CLONE_DIR}" && cd "${CLONE_DIR}" || _abort
2549

2650
# Add origin
2751
[ -d "${CLONE_DIR}"/.git/ ] \
28-
&& notif err "${CLONE_DIR} is already a git directory." && abort \
52+
&& _notif err "${CLONE_DIR} is already a git directory." && _abort \
2953
|| git init
3054
GIT_URL=${GIT_HOST}.com/${REPO_OWNER}/${REPO_NAME}
3155
[ ${GIT_USER} ] && [ ${GIT_TOKEN} ] \
3256
&& git remote add origin https://${GIT_USER}:${GIT_TOKEN}@${GIT_URL}.git \
3357
|| git remote add origin https://${GIT_URL}.git
3458

35-
enable-partial-clone ${CLONE_DIR} ${REMOTE_PARTIAL_DIR}
36-
fetch-commit-history ${CLONE_DIR} ${COMMIT_DEPTH}
59+
_enable-partial-clone ${CLONE_DIR} ${REMOTE_PARTIAL_DIR}
60+
_fetch-commit-history ${CLONE_DIR} "${COMMIT_DEPTH}"
3761

3862
# Pull branch(es)
3963
[ ${BRANCH} ] \
40-
&& { notif ok "Trying to fetch branch ${BRANCH}" && \
41-
pull-single-branch ${CLONE_DIR} ${BRANCH} ;} \
42-
|| { notif warn "BRANCH not specified, pulling every branch in ${REPO_NAME}." && \
43-
pull-all-branches ${CLONE_DIR} ;}
64+
&& { _notif ok "Trying to fetch branch ${BRANCH}" && \
65+
_pull-single-branch ${CLONE_DIR} ${BRANCH} ;} \
66+
|| { _notif warn "BRANCH not specified, pulling every branch in ${REPO_NAME}." && \
67+
_pull-all-branches ${CLONE_DIR} ;}
4468

4569
# Done
4670
[ ${REMOTE_PARTIAL_DIR} ] \
47-
&& notif ok "${REMOTE_PARTIAL_DIR} of https://${GIT_URL} was cloned into" \
48-
|| notif ok "https://${GIT_URL} was cloned into"
49-
notif ok "${CLONE_DIR}"
50-
cd - && unset-variables-from-file ${1}
71+
&& _notif ok "${REMOTE_PARTIAL_DIR} of https://${GIT_URL} was cloned into" \
72+
|| _notif ok "https://${GIT_URL} was cloned into"
73+
_notif ok "${CLONE_DIR}"
74+
cd - && _unset-variables-from-file ${1}
5175
}
5276

53-
check-mandatory-vars() {
77+
_check-mandatory-vars() {
5478
# Returns an error if a mandatory variable is missing.
55-
# Usage: check-mandatory-vars ${STRING_OF_SPACE_SEPARATED_VAR_NAMES}
79+
# Usage: _check-mandatory-vars ${STRING_OF_SPACE_SEPARATED_VAR_NAMES}
5680
local vars_arr=($1)
5781
local count=0
5882
for var_name in "${vars_arr[@]}"; do
5983
local var_value="${!var_name}"
6084
# echo "$var_name=${var_value}"
6185
[ "${var_value}" ] \
62-
|| { notif err "$var_name is mandatory." && ((++count)) ;}
86+
|| { _notif err "$var_name is mandatory." && ((++count)) ;}
6387
done
6488
[[ $count -eq 0 ]] && return 0 || return 1
6589
}
6690

67-
get-token-from-file() {
91+
_get-token-from-file() {
6892
# Reads the contents of the token file
69-
# Usage: get-token-from-file <path to token file> <OUTPUT_VARIABLE_NAME>
93+
# Usage: _get-token-from-file <path to token file> <OUTPUT_VARIABLE_NAME>
7094
eval TOKEN_PATH="${1}" # expand quoted path
7195
MSG_NO_TOKEN="The repository must be public in order to be cloned."
7296
MSG_TOKEN_PROVIDED="A token was found! The repository will be cloned if you have access to it."
7397

7498
[[ -z $TOKEN_PATH ]] \
75-
&& notif warn "You did not provide a token. ${MSG_NO_TOKEN}" \
99+
&& _notif warn "You did not provide a token. ${MSG_NO_TOKEN}" \
76100
|| { MY_TOKEN=$(cat ${TOKEN_PATH}) && [ ${MY_TOKEN} ] \
77-
&& eval "${2}='${MY_TOKEN}'" && notif ok "${MSG_TOKEN_PROVIDED}" \
78-
|| notif err "Could not find a token in ${TOKEN_PATH}. ${MSG_NO_TOKEN}" ;}
101+
&& eval "${2}='${MY_TOKEN}'" && _notif ok "${MSG_TOKEN_PROVIDED}" \
102+
|| _notif err "Could not find a token in ${TOKEN_PATH}. ${MSG_NO_TOKEN}" ;}
79103
}
80104

81-
get-clone-dir-path() {
105+
_get-clone-dir-path() {
82106
# Returns the path where the repository will be cloned.
83-
# Usage: get-clone-dir-path <parent path> <name of repository> <OUTPUT_VARIABLE_NAME>
107+
# Usage: _get-clone-dir-path <parent path> <name of repository> <OUTPUT_VARIABLE_NAME>
84108
local PARENT_DIR="${1}"
85109
local REPO_NAME="${2}"
86-
[[ -z "${PARENT_DIR}" ]] && PARENT_DIR=${PWD} && notif warn "PARENT_DIR is blank"
110+
[[ -z "${PARENT_DIR}" ]] && PARENT_DIR=${PWD} && _notif warn "PARENT_DIR is blank"
87111

88112
# Convert to absolute
89113
[[ "${PARENT_DIR:0:1}" != "/" ]] && PARENT_DIR=${PWD}/${PARENT_DIR}
@@ -93,52 +117,52 @@ get-clone-dir-path() {
93117

94118
mkdir -p "${PARENT_DIR}" && [ -d "${PARENT_DIR}" ] \
95119
&& eval "${3}='${PARENT_DIR}/${REPO_NAME}'" \
96-
&& notif ok "The repository will be cloned within ${PARENT_DIR}" && return 0 \
97-
|| { notif err "${PARENT_DIR} does not exist." && return 1 ;}
120+
&& _notif ok "The repository will be cloned within ${PARENT_DIR}" && return 0 \
121+
|| { _notif err "${PARENT_DIR} does not exist." && return 1 ;}
98122
}
99123

100-
get-variables-from-file() {
124+
_get-variables-from-file() {
101125
# Set the variables contained in a file of key-value pairs
102126
export $(grep --invert-match '^#' ${1} | xargs -d '\n')
103127
}
104128

105-
unset-variables-from-file() {
129+
_unset-variables-from-file() {
106130
# Removes variables contained in a file of key-value pairs
107131
unset $(grep --invert-match '^#' ${1} | \
108132
grep --perl-regexp --only-matching '.*(?=\=)' | xargs)
109133
}
110134

111-
enable-partial-clone() {
135+
_enable-partial-clone() {
112136
# Enable partial cloning if a subfolder is provided
113137
local CLONE_DIR=${1}
114138
local REMOTE_PARTIAL_DIR=${2}
115139
git -C ${CLONE_DIR} config --local extensions.partialClone origin
116140
[ ${REMOTE_PARTIAL_DIR} ] && git -C ${CLONE_DIR} sparse-checkout set ${REMOTE_PARTIAL_DIR}
117141
}
118142

119-
fetch-commit-history() {
143+
_fetch-commit-history() {
120144
# Fetch history according to the provided commit depth
121145
local CLONE_DIR="${1}"
122146
local COMMIT_DEPTH="${2}"
123147
[ ${COMMIT_DEPTH} ] && [ ${COMMIT_DEPTH} -eq ${COMMIT_DEPTH} ] \
124-
&& { notif warn "Using COMMIT_DEPTH=${COMMIT_DEPTH}." \
148+
&& { _notif warn "Using COMMIT_DEPTH=${COMMIT_DEPTH}." \
125149
&& git -C ${CLONE_DIR} fetch --depth ${COMMIT_DEPTH} --filter=blob:none \
126-
|| abort clean ;} \
127-
|| { notif warn "COMMIT_DEPTH not provided, fetching all of the history." \
150+
|| _abort clean ;} \
151+
|| { _notif warn "COMMIT_DEPTH not provided, fetching all of the history." \
128152
&& git -C ${CLONE_DIR} fetch --filter=blob:none \
129-
|| abort clean ;}
153+
|| _abort clean ;}
130154
}
131155

132-
pull-single-branch() {
156+
_pull-single-branch() {
133157
local CLONE_DIR=${1}
134158
local BRANCH=${2}
135159
git -C ${CLONE_DIR} checkout -b $BRANCH
136160
git -C ${CLONE_DIR} pull origin $BRANCH \
137161
&& git -C ${CLONE_DIR} branch --set-upstream-to=origin/$BRANCH ${BRANCH} \
138-
|| abort clean
162+
|| _abort clean
139163
}
140164

141-
pull-all-branches() {
165+
_pull-all-branches() {
142166
# Pull every branch in the remote
143167
# and switch to the default branch
144168
local CLONE_DIR=${1}
@@ -164,8 +188,8 @@ pull-all-branches() {
164188
git checkout ${HEAD_BRANCH}
165189
}
166190

167-
notif() {
168-
# Usage: notif <status> <message>
191+
_notif() {
192+
# Usage: _notif <status> <message>
169193
local info='\033[0m'
170194
local ok='\033[0;32m'
171195
local warn='\033[0;33m'
@@ -176,17 +200,17 @@ notif() {
176200
printf ${info}
177201
}
178202

179-
abort() {
180-
notif err "Aborted."
203+
_abort() {
204+
_notif err "Aborted."
181205
case $# in
182206
1)
183-
notif warn "Removing empty tree in ${CLONE_DIR}"
207+
_notif warn "Removing empty tree in ${CLONE_DIR}"
184208
rm -rf ${CLONE_DIR} && \
185209
rmdir -p --ignore-fail-on-non-empty ${CLONE_DIR%/*}
186210
;;
187211
esac
188212
exit
189213
}
190214

191-
git-partial-clone ${CONFIG_FILE_PATH}
215+
main "$@"
192216

install.sh

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#!/bin/bash
2+
3+
#
4+
# Installer for the git-partial-clone script
5+
#
6+
# Copyright (c) 2021 Lucero Alvarado
7+
# https://github.com/lu0/git-partial-clone
8+
#
9+
10+
echo "Installing git-partial-clone ..."
11+
LOCAL_PATH=~/.local/bin
12+
BASH_COMPLETION_DIR=~/.local/etc/bash_completion.d
13+
BASH_COMPLETION_SCRIPT=~/.bash_completion
14+
KEYWORDS_DIR=~/.config/git-partial-clone
15+
16+
echo "Adding script to the local PATH ..."
17+
ln -srf git-partial-clone.sh ${LOCAL_PATH}/git-partial-clone
18+
19+
echo "Adding the completion rules ..."
20+
mkdir -p ${BASH_COMPLETION_DIR}
21+
ln -srf completion-rules.sh ${BASH_COMPLETION_DIR}/git-partial-clone
22+
mkdir -p ${KEYWORDS_DIR}
23+
ln -srf keywords ${KEYWORDS_DIR}/
24+
echo "for file in ${BASH_COMPLETION_DIR}/* ; do" >> ${BASH_COMPLETION_SCRIPT}
25+
echo -e "\t. \$file" >> ${BASH_COMPLETION_SCRIPT}
26+
echo "done" >> ${BASH_COMPLETION_SCRIPT}
27+
echo "Done!"
28+
29+
exec bash
30+

uninstall.sh

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#!/bin/bash
2+
3+
#
4+
# Uninstaller for the git-partial-clone script
5+
#
6+
# Copyright (c) 2021 Lucero Alvarado
7+
# https://github.com/lu0/git-partial-clone
8+
#
9+
10+
echo "Uninstalling git-partial-clone ..."
11+
LOCAL_PATH=~/.local/bin
12+
BASH_COMPLETION_DIR=~/.local/etc/bash_completion.d
13+
BASH_COMPLETION_SCRIPT=~/.bash_completion
14+
KEYWORDS_DIR=~/.config/git-partial-clone
15+
16+
echo "Removing script from the local PATH ..."
17+
rm -rf ${LOCAL_PATH}/git-partial-clone
18+
19+
echo "Removing the completion rules ..."
20+
LINES_IN_FILE=$([[ -f ~/.bash_completion ]] \
21+
&& sort ~/.bash_completion | uniq | wc -l)
22+
[[ ${LINES_IN_FILE} == 3 ]] \
23+
&& rm -rf ${BASH_COMPLETION_DIR} \
24+
&& rm -rf ~/.bash_completion \
25+
|| rm -rf ${BASH_COMPLETION_SCRIPT}
26+
rm -rf ${KEYWORDS_DIR}
27+
echo "Done!"
28+
29+
exec bash
30+

0 commit comments

Comments
 (0)