-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Migrate and refactor from lu0/git-scripts/git-partial-clone to make i…
…t a submodule.
- Loading branch information
0 parents
commit 128f118
Showing
6 changed files
with
977 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
tmp/ | ||
releases/ | ||
|
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
`git-partial-clone` | ||
--- | ||
|
||
This script clones a subdirectory of a github/gitlab repository. | ||
|
||
# Usage | ||
## Quick test | ||
Test the script with the example config file. | ||
By the end of the execution, you will see a `tmp` directory containing the subfolder of the example repository. | ||
```zsh | ||
./git-partial-clone.sh example.conf | ||
``` | ||
## Install | ||
Add the script to your `PATH` | ||
```zsh | ||
ln -srf git-partial-clone.sh ~/.local/bin/git-partial-clone | ||
``` | ||
Then you can execute the script from any directory with your custom config file. | ||
```zsh | ||
git-partial-clone path/to/your/config/file.conf | ||
``` | ||
|
||
# Configuration | ||
Fill in the config file ([`template.conf`](./template.conf)) with the information of the repository you're cloning. You can see the example file [here](./example.conf). | ||
|
||
## Mandatory variables | ||
|
||
- `GIT_HOST`: | ||
- `github` if the repository is hosted on Github. | ||
- `gitlab` if the repository is hosted on Gitlab. | ||
- `REPO_OWNER`: | ||
- Username of the owner/author of the repository. | ||
- `REPO_NAME`: | ||
- Name of the repository to be cloned. | ||
- **`REMOTE_PARTIAL_DIR`**: | ||
- **Subdirectory of the repository you want to clone**. | ||
- Omit it to clone the entire repository. | ||
|
||
## Mandatory variables for **private repositories** | ||
You will need to generate an access token in order to clone private repositories, as password authentication is deprecated. | ||
|
||
- Github: [github.com/settings/tokens](https://github.com/settings/tokens). | ||
- Gitlab: [gitlab.com/-/profile/personal_access_tokens](https://gitlab.com/-/profile/personal_access_tokens). | ||
|
||
Once you have a token, store it in a file. | ||
|
||
- `TOKEN_PATH`: | ||
- Path to the file containing the access token. | ||
- `GIT_USER`: | ||
- Username with access to the repository. | ||
|
||
## Optional variables | ||
The following variables give you more control over the objects you're cloning. | ||
- `BRANCH`: | ||
- The branch to be fetched. | ||
- Omit it to pull all of the branches and switch to the default one. | ||
- `COMMIT_DEPTH`: | ||
- Number of commits you want to fetch (useful for deployment purposes). | ||
- Omit it to fetch the entire remote history. | ||
- `PARENT_DIR`: | ||
- Path to the target parent directory. | ||
- Omit it to clone the repository in the current directory. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# | ||
# Example of the git-partial-clone configuration file | ||
# | ||
# Copyright (c) 2021 Lucero Alvarado | ||
# https://github.com/lu0/git-partial-clone | ||
# | ||
# THIS FILE DOES NOT SUPPORT INLINE COMMENTS! | ||
# | ||
|
||
GIT_HOST=github | ||
REPO_OWNER=lu0 | ||
REPO_NAME=vscode-settings | ||
REMOTE_PARTIAL_DIR=json/snippets | ||
|
||
TOKEN_PATH= | ||
GIT_USER= | ||
|
||
BRANCH= | ||
COMMIT_DEPTH=3 | ||
|
||
PARENT_DIR=tmp | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
#!/bin/bash | ||
|
||
# | ||
# The git-partial-clone script | ||
# Clone a subdirectory of a github/gitlab repository | ||
# | ||
# Copyright (c) 2021 Lucero Alvarado | ||
# https://github.com/lu0/git-partial-clone | ||
# | ||
|
||
CONFIG_FILE_PATH=${1} | ||
|
||
git-partial-clone() { | ||
# Source config file | ||
[ ${1} ] \ | ||
&& get-variables-from-file ${1} \ | ||
|| { notif err "git-partial-clone requires a configuration file." && abort ;} | ||
|
||
check-mandatory-vars "GIT_HOST REPO_NAME REPO_OWNER" || abort | ||
get-token-from-file "${TOKEN_PATH}" GIT_TOKEN | ||
|
||
# Change working directory | ||
get-clone-dir-path "${PARENT_DIR}" "${REPO_NAME}" CLONE_DIR || abort | ||
mkdir "${CLONE_DIR}" && cd "${CLONE_DIR}" || abort | ||
|
||
# Add origin | ||
[ -d "${CLONE_DIR}"/.git/ ] \ | ||
&& notif err "${CLONE_DIR} is already a git directory." && abort \ | ||
|| git init | ||
GIT_URL=${GIT_HOST}.com/${REPO_OWNER}/${REPO_NAME} | ||
[ ${GIT_USER} ] && [ ${GIT_TOKEN} ] \ | ||
&& git remote add origin https://${GIT_USER}:${GIT_TOKEN}@${GIT_URL}.git \ | ||
|| git remote add origin https://${GIT_URL}.git | ||
|
||
enable-partial-clone ${CLONE_DIR} ${REMOTE_PARTIAL_DIR} | ||
fetch-commit-history ${CLONE_DIR} ${COMMIT_DEPTH} | ||
|
||
# Pull branch(es) | ||
[ ${BRANCH} ] \ | ||
&& { notif ok "Trying to fetch branch ${BRANCH}" && \ | ||
pull-single-branch ${CLONE_DIR} ${BRANCH} ;} \ | ||
|| { notif warn "BRANCH not specified, pulling every branch in ${REPO_NAME}." && \ | ||
pull-all-branches ${CLONE_DIR} ;} | ||
|
||
# Done | ||
[ ${REMOTE_PARTIAL_DIR} ] \ | ||
&& notif ok "${REMOTE_PARTIAL_DIR} of https://${GIT_URL} was cloned into" \ | ||
|| notif ok "https://${GIT_URL} was cloned into" | ||
notif ok "${CLONE_DIR}" | ||
cd - && unset-variables-from-file ${1} | ||
} | ||
|
||
check-mandatory-vars() { | ||
# Returns an error if a mandatory variable is missing. | ||
# Usage: check-mandatory-vars ${STRING_OF_SPACE_SEPARATED_VAR_NAMES} | ||
local vars_arr=($1) | ||
local count=0 | ||
for var_name in "${vars_arr[@]}"; do | ||
local var_value="${!var_name}" | ||
# echo "$var_name=${var_value}" | ||
[ "${var_value}" ] \ | ||
|| { notif err "$var_name is mandatory." && ((++count)) ;} | ||
done | ||
[[ $count -eq 0 ]] && return 0 || return 1 | ||
} | ||
|
||
get-token-from-file() { | ||
# Reads the contents of the token file | ||
# Usage: get-token-from-file <path to token file> <OUTPUT_VARIABLE_NAME> | ||
eval TOKEN_PATH="${1}" # expand quoted path | ||
MSG_NO_TOKEN="The repository must be public in order to be cloned." | ||
MSG_TOKEN_PROVIDED="A token was found! The repository will be cloned if you have access to it." | ||
|
||
[[ -z $TOKEN_PATH ]] \ | ||
&& notif warn "You did not provide a token. ${MSG_NO_TOKEN}" \ | ||
|| { MY_TOKEN=$(cat ${TOKEN_PATH}) && [ ${MY_TOKEN} ] \ | ||
&& eval "${2}='${MY_TOKEN}'" && notif ok "${MSG_TOKEN_PROVIDED}" \ | ||
|| notif err "Could not find a token in ${TOKEN_PATH}. ${MSG_NO_TOKEN}" ;} | ||
} | ||
|
||
get-clone-dir-path() { | ||
# Returns the path where the repository will be cloned. | ||
# Usage: get-clone-dir-path <parent path> <name of repository> <OUTPUT_VARIABLE_NAME> | ||
local PARENT_DIR="${1}" | ||
local REPO_NAME="${2}" | ||
[[ -z "${PARENT_DIR}" ]] && PARENT_DIR=${PWD} && notif warn "PARENT_DIR is blank" | ||
|
||
# Convert to absolute | ||
[[ "${PARENT_DIR:0:1}" != "/" ]] && PARENT_DIR=${PWD}/${PARENT_DIR} | ||
|
||
# Remove leading slash | ||
[[ "${PARENT_DIR}" == */ ]] && PARENT_DIR="${PARENT_DIR: : -1}" | ||
|
||
mkdir -p "${PARENT_DIR}" && [ -d "${PARENT_DIR}" ] \ | ||
&& eval "${3}='${PARENT_DIR}/${REPO_NAME}'" \ | ||
&& notif ok "The repository will be cloned within ${PARENT_DIR}" && return 0 \ | ||
|| { notif err "${PARENT_DIR} does not exist." && return 1 ;} | ||
} | ||
|
||
get-variables-from-file() { | ||
# Set the variables contained in a file of key-value pairs | ||
export $(grep --invert-match '^#' ${1} | xargs -d '\n') | ||
} | ||
|
||
unset-variables-from-file() { | ||
# Removes variables contained in a file of key-value pairs | ||
unset $(grep --invert-match '^#' ${1} | \ | ||
grep --perl-regexp --only-matching '.*(?=\=)' | xargs) | ||
} | ||
|
||
enable-partial-clone() { | ||
# Enable partial cloning if a subfolder is provided | ||
local CLONE_DIR=${1} | ||
local REMOTE_PARTIAL_DIR=${2} | ||
[ ${REMOTE_PARTIAL_DIR} ] \ | ||
&& git -C ${CLONE_DIR} config --local extensions.partialClone origin \ | ||
&& git -C ${CLONE_DIR} sparse-checkout set ${REMOTE_PARTIAL_DIR} | ||
} | ||
|
||
fetch-commit-history() { | ||
# Fetch history according to the provided commit depth | ||
local CLONE_DIR="${1}" | ||
local COMMIT_DEPTH="${2}" | ||
[ ${COMMIT_DEPTH} ] && [ ${COMMIT_DEPTH} -eq ${COMMIT_DEPTH} ] \ | ||
&& { notif warn "Using COMMIT_DEPTH=${COMMIT_DEPTH}." \ | ||
&& git -C ${CLONE_DIR} fetch --depth ${COMMIT_DEPTH} --filter=blob:none \ | ||
|| abort clean ;} \ | ||
|| { notif warn "COMMIT_DEPTH not provided, fetching all of the history." \ | ||
&& git -C ${CLONE_DIR} fetch --filter=blob:none \ | ||
|| abort clean ;} | ||
} | ||
|
||
pull-single-branch() { | ||
local CLONE_DIR=${1} | ||
local BRANCH=${2} | ||
git -C ${CLONE_DIR} checkout -b $BRANCH | ||
git -C ${CLONE_DIR} pull origin $BRANCH \ | ||
&& git -C ${CLONE_DIR} branch --set-upstream-to=origin/$BRANCH ${BRANCH} \ | ||
|| abort clean | ||
} | ||
|
||
pull-all-branches() { | ||
# Pull every branch in the remote | ||
# and switch to the default branch | ||
local CLONE_DIR=${1} | ||
|
||
# Create empty branches | ||
N_BRANCHES=$(git -C ${CLONE_DIR} branch -r | wc -l) | ||
for ((i=1; i<=$N_BRANCHES; i++)); do | ||
BRANCH_NAME_i=$(git -C ${CLONE_DIR} branch -r | \ | ||
head -$i | tail -1 | sed s/"origin\/"// | xargs) | ||
git -C ${CLONE_DIR} checkout -b $BRANCH_NAME_i | ||
done | ||
|
||
# Pull and track every branch | ||
for ((i=1; i<=$N_BRANCHES; i++)); do | ||
CURRENT_BRANCH=$(git -C ${CLONE_DIR} branch -r | \ | ||
head -$i | tail -1 | sed s/"origin\/"// | xargs) | ||
git -C ${CLONE_DIR} checkout $CURRENT_BRANCH | ||
git -C ${CLONE_DIR} pull origin $CURRENT_BRANCH | ||
git -C ${CLONE_DIR} branch --set-upstream-to=origin/$CURRENT_BRANCH ${CURRENT_BRANCH} | ||
done | ||
HEAD_BRANCH=$(git -C ${CLONE_DIR} remote show origin | \ | ||
grep --perl-regexp --only-matching '(?<=HEAD branch: ).*') | ||
git checkout ${HEAD_BRANCH} | ||
} | ||
|
||
notif() { | ||
# Usage: notif <status> <message> | ||
local info='\033[0m' | ||
local ok='\033[0;32m' | ||
local warn='\033[0;33m' | ||
local err='\033[0;31m' | ||
local STATUS=${!1} | ||
local MSG="${2}" | ||
printf $STATUS"${MSG}\n" | ||
printf ${info} | ||
} | ||
|
||
abort() { | ||
notif err "Aborted." | ||
case $# in | ||
1) | ||
notif warn "Removing empty tree in ${CLONE_DIR}" | ||
rm -rf ${CLONE_DIR} && \ | ||
rmdir -p --ignore-fail-on-non-empty ${CLONE_DIR%/*} | ||
;; | ||
esac | ||
exit | ||
} | ||
|
||
git-partial-clone ${CONFIG_FILE_PATH} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# | ||
# Example of the git-partial-clone configuration file | ||
# | ||
# Copyright (c) 2021 Lucero Alvarado | ||
# https://github.com/lu0/git-partial-clone | ||
# | ||
# THIS FILE DOES NOT SUPPORT INLINE COMMENTS! | ||
# | ||
|
||
GIT_HOST= | ||
REPO_OWNER= | ||
REPO_NAME= | ||
REMOTE_PARTIAL_DIR= | ||
|
||
TOKEN_PATH= | ||
GIT_USER= | ||
|
||
BRANCH= | ||
COMMIT_DEPTH= | ||
|
||
PARENT_DIR= | ||
|