jdm
jdm

Reputation: 10130

Is there a way to download only a subdirectory from git?

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

Answers (3)

AIe
AIe

Reputation: 21

A full paramateric bash function (no-dependancy)

#!/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

david_adler
david_adler

Reputation: 10972

Hey I just wrote a script

usage

      python get_git_sub_dir.py path/to/sub/dir <RECURSIVE>

Upvotes: 0

Alex Howansky
Alex Howansky

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

Related Questions