Easily Create (Clean) Compressed Tarballs of Your Git Repository
Ideally, you could refer the whole world --- or at least, the significant portion thereof that want your code --- to your (public mirror) Git repository. But unfortunately, the whole world does not (yet) use Git ("I know it was you Fredo, I know it was you, and it breaks my heart."). Sad. Sooooo sad. But true. So the only recourse is for you to send these tortured souls an archived snapshot of your code via e-mail. I'll pause now to let you finish retching/sobbing/lamenting/venting. ... Back? Anyway, Git has a neat "archive" command that helps you create the required archive, but perhaps it does not have the most friendliest interface in the world. I've written some scripts to wrap the Git command to facilitate its use, which I share here.
Update 2011-07-07: I have re-written the original three scripts previously described here as a single script, incorporating some of the excellent suggestions that kind folks provided in the comments. Immediately below this is the new, unified, all-bells-and-whistles-included script. Simply name it something like "git-makedist", switch on its executable bit, and place it somewhere on your system path. Reasonable names, filepaths, prefixes, etc. are all provided if you do not explicitly specify them. All of which means that, in most cases, once installed, simply invoking the command "git makedist" without any options or arguments from within a Git repository "just works (tm)", resulting in a sensibly-named compressed archive of the current HEAD ready for distribution. At the same time, various flags and options allow you to fine-tune the operations, e.g. determine the compression/archive method, the path prefixes, the archive name, etc.
Update 2011-07-22, Excluding Files:
Sometimes, not all files need to make it out to an archive. You can use the ".gitattributes" file to control which files get archived. Simply add the paths/names/globs of the files or directories you would like to exclude, and add a "export-ignore" after it. For example:
.gitattributes export-ignore
.gitignore export-ignore
*.tmp export-ignore
Note that the ".gitattributes" file has to be committed for it to have an effect.
#! /bin/bash compose_name() { if [[ $(which git 2> /dev/null) ]] then STATUS=$(git status 2>/dev/null) if [[ -z $STATUS ]] then return fi TARGET="./$(git rev-parse --show-cdup)$1" cd $TARGET echo "$(basename $(pwd))" cd - > /dev/null return else return fi } function usage { echo "`basename $0` [OPTIONS]" echo "Archives repository tree." echo " " echo "Options:" echo " -f|--format FORMAT ... Archive format ([targz], tarbz, zip)" echo " -r|--revision REV ... Revision (default=HEAD)" echo " -p|--prefix PREFIX ... prefix to add before directory paths in archive" echo " (default = repository name)" echo " --no-prefix ... suppress prefix before directory paths in archive" echo " -n|--name NAME ... Archive file basename" echo " (default = repository name)" echo " -o|--output DIR ... Archive output directory (default = current)" echo " -h|--help ... Show help" exit 1 } NOPREFIX=0 while [ -n $1 ]; do case $1 in -f|--format) FORMAT=$2 shift 2 ;; -r|--revision) REVISION=$2 shift 2 ;; --no-prefix) NOPREFIX=1 shift ;; -p|--prefix) PREFIX=$2 shift 2 ;; -n|--name) REPONAME=$2 shift 2 ;; -o|--output) OUTPUTDIR=$2 shift 2 ;; -h|--help) usage ;; *) break ;; esac done if [[ -z $FORMAT ]] then FORMAT="targz" fi if [[ -z "$REVISION" ]] then REVISION="HEAD" fi if [[ -z $REPONAME ]] then REPONAME=$(compose_name) if [[ -z $REPONAME ]] then echo "FATAL: not a Git repository, or could not parse repository name (supply using '-p'/'--prefix' flag)" exit 1 fi fi if [[ -z "$OUTPUTDIR" ]] then OUTPUTDIR="." fi if [[ $FORMAT == "zip" ]] then GITARCHIVEFORMAT="zip" EXT=".zip" else GITARCHIVEFORMAT="tar" if [[ $FORMAT == "tarbz" ]] then EXT=".tar.bz2" COMPRESS="bzip2" elif [[ $FORMAT == "targz" ]] then EXT=".tar.gz" COMPRESS="gzip" else echo "FATAL: Unrecognized format '$FORMAT'" exit 1 fi fi if [[ $NOPREFIX == 1 ]] then PREFIXCOMMAND="" elif [[ -z "$PREFIX" ]] then PREFIXCOMMAND="--prefix=$REPONAME/" else PREFIXCOMMAND="--prefix=$PREFIX/" fi ARCHIVEBASENAME="$REPONAME-$(git describe --always)" OUTPUTPATH="$OUTPUTDIR/$ARCHIVEBASENAME$EXT" if [[ $FORMAT == "zip" ]] then git archive --format="$GITARCHIVEFORMAT" $PREFIXCOMMAND $REVISION > $OUTPUTPATH else git archive --format="$GITARCHIVEFORMAT" $PREFIXCOMMAND $REVISION | $COMPRESS >$OUTPUTPATH fi
feed
Comments
5 comments postedThanks for this script - its really useful that I didn't have to write it my self :-)
But my use case is that I often need to tar arbitrary repositories that I may or may not already have a clone of them on my local drive. As a result, using your script as is I need to first clone the repository, cd into it, ran the script, cd out and remove the directory. lather rinse repeat.
I've modified the script a bit so that it takes an optional "source" argument. If non specified then it behaves as it does now, but if a source is provided it checks if the source is a local directory path, in which case it changes to that directory before continuing. If the source is a URL to a git repository, the script will now create a temporary directory and clone the git repository there (and will remove it when the script is done.
Please see here for the updated version: http://pastebin.com/1yZZBup8
I like it to name the file for me, so I use "git describe --always".
https://gist.github.com/965475
That's a nice approach, Tim. I am going to use it.
Nice. You don't mention it, but you can drop these scripts in your libexec/git-core directory and call them as "git targz", etc.
I made a few changes to git-targz (the one that I will likely use) to make it work under standard /bin/sh here: http://gist.github.com/376455 (because not everybody has bash!)
Yes, I'm a little fuzzy on where 'sh' ends and 'bash' begins. Your tweak is definitely useful in that regard.
It would be nice if the script could default to a sensible name if none is given (e.g. "-", so you might get "project-ff3021"). But, as I mentioned, if I extend these scripts any more, it will probably have to be in Python!
I did not know about lib-exec: thanks for pointing that out. However, any executable script on your $PATH that has a "git-XXXX" pattern can be invoked via "git XXXX". So, for example, if '~/bin' is in your $PATH, then a executable script called "~/bin/git-foo" can be invoked via "git foo".
Post new comment