Reputation: 10130
Is there a way to download only a given subdirectory from a git repository? Say I want to fetch only the nyancat
directory from https://github.com/djdeath/toys.git .
I could either clone the whole repository and ignore the files I don't want, or go to https://github.com/djdeath/toys/tree/master/nyancat and download the relevant files one by one. I think there must be an easier way.
Note I'm not asking if it is possible to clone the directory, this was asked before and is apparently not possible. I'm just interested in quickly getting the files, and don't need to commit back, or use git on them again.
Upvotes: 4
Views: 1246
Reputation: 21
#!/bin/bash
gitsub() {
usage() {
cat <<- EOF
------------------------------------------------------------------------
GNU gitsub 0.0.1, a non-interactive github filtered subrepo retriever.
Usage: gitsub [-h] [[-d[dir] -s[strip] -e[ext]] -o owner -r repo -b sub]
------------------------------------------------------------------------
Mandatory arguments to long options are mandatory for short options too.
------------------------------------------------------------------------
MANDATORY:
-o, --owner repo's owner
-r, --repo repo's name
-b, --subrepo directory(s) to be cloned
OPTIONS:
-s, --strip number of dirs (/) to be skipped, default 0
-d, --dir output directory default current directory
-e, --extension filter by ext, if missing clone all (including subdirs)
COMMANDS:
-h, --help display this help and exit
------------------------------------------------------------------------
Mail bug reports and suggestions to [email protected]
------------------------------------------------------------------------
EOF
}
error() { echo -e "\033[1;31mError: $1\033[0m" ;}
# check supplied args
is_arg() {
[[ -n "$2" && ${2:0:1} != "-" ]] \
|| { error "Argument for $1 is missing..." >&2 \
&& usage \
&& exit 1 ;}
}
POSITIONAL=()
while (( "$#" )); do
case "$1" in
# commands
-h|--help) usage && exit 0 ;;
# mandatory flags with arguments
-o|--owner) is_arg $1 $2 && OWNER=$2 ; shift 2 ;;
-r|--repo) is_arg $1 $2 && REPO=$2 ; shift 2 ;;
-b|--subrepo) is_arg $1 $2 && SUBREPO=$2 ; shift 2 ;;
# optional flags with arguments
-d|--dir) is_arg $1 $2 && DIR=$2 && mkdir -p $DIR ; shift 2 ;;
-s|--strip) is_arg $1 $2 && STRIP=$2 ; shift 2 ;;
-e|--extension) is_arg $1 $2 && EXTENSION=$2 ; shift 2 ;;
# unsupported flags
-*|--*=) error "Unsupported flag $1" >&2 && usage ; exit 1 ;;
# preserve positional arguments
*) POSITIONAL+=("$1") ; shift ;;
esac
done
# set positional arguments in their proper place
set -- "${POSITIONAL[@]}"
# check mandatory arguments
[[ -z "$OWNER" || -z "$REPO" || -z "$SUBREPO" ]] \
&& { error "Missing mandatory arguments..." >&2 \
&& usage \
&& exit 1 ;}
# get github filtered (optional) subrepository
{
curl -L "https://api.github.com/repos/$OWNER/$REPO/tarball" \
|
tar \
--verbose \
--extract \
--gzip \
--directory=${DIR:-$PWD} \
--strip=${STRIP:-0} \
--wildcards */${SUBREPO}/*.${EXTENSION}*
} 2>/dev/null \
&& echo "Done" \
|| \
{
error "Invalid args..." \
&& usage \
&& exit 1
}
}
gitsub "$@"
For a given repo: https://github.com/jenskutilek/free-fonts
To download the entire contents of the sub-folder Fira including dirs and files
$ bash gitsub.sh -o "jenskutilek" -r "free-fonts" -b "Fira" -d "FiraUnfiltered" -s 2
$ tree -d FiraUnfiltered/
FiraUnfiltered/
├── Fira Mono
│ ├── OTF
│ ├── TTF
│ ├── VFB
│ └── WOFF
└── Fira Sans
├── OTF
├── TTF
├── VFB
└── WOFF
To downlod the same subfolder but filtered with the font TTF
$ bash gitsub.sh -o "jenskutilek" -r "free-fonts" -b "Fira" -d "FiraFiltered" -s 2 -e "ttf"
$ tree -d FiraFiltered/
FiraFiltered/
├── Fira Mono
│ └── TTF
└── Fira Sans
└── TTF
downlod only the filtered files to the outdir by setting -s|--strip to 4
bash gitsub.sh -o "jenskutilek" -r "free-fonts" -b "Fira" -d "ttfFilesOnly" -s 4 -e "ttf"
$ tree ttfFilesOnly/
ttfFilesOnly/
├── FiraMono-Bold.ttf
├── FiraMono-Regular.ttf
├── FiraSans-Bold.ttf
├── FiraSans-BoldItalic.ttf
├── FiraSans-Light.ttf
├── FiraSans-LightItalic.ttf
├── FiraSans-Medium.ttf
├── FiraSans-MediumItalic.ttf
├── FiraSans-Regular.ttf
└── FiraSans-RegularItalic.ttf
Upvotes: 1
Reputation: 10972
Hey I just wrote a script
usage
python get_git_sub_dir.py path/to/sub/dir <RECURSIVE>
Upvotes: 0
Reputation: 53626
The git-archive
command will do pretty much what you want, but it needs to operate on an already cloned repo, so if you've got SSH access to the host, you can do:
cd /path/to/destination
ssh user@host "cd /path/to/repo && git archive HEAD dir/you/want" | tar xf -
Or, with compression for the network transport:
cd /path/to/destination
ssh user@host "cd /path/to/repo && git archive HEAD dir/you/want | gzip" | tar xzf -
Upvotes: 2