Do a "git export" (like "svn export")?

Viewed 715762

I've been wondering whether there is a good "git export" solution that creates a copy of a tree without the .git repository directory. There are at least three methods I know of:

  1. git clone followed by removing the .git repository directory.
  2. git checkout-index alludes to this functionality but starts with "Just read the desired tree into the index..." which I'm not entirely sure how to do.
  3. git-export is a third-party script that essentially does a git clone into a temporary location followed by rsync --exclude='.git' into the final destination.

None of these solutions really strike me as being satisfactory. The closest one to svn export might be option 1, because both require the target directory to be empty first. But option 2 seems even better, assuming I can figure out what it means to read a tree into the index.

31 Answers

Probably the simplest way to achieve this is with git archive. If you really need just the expanded tree you can do something like this.

git archive master | tar -x -C /somewhere/else

Most of the time that I need to 'export' something from git, I want a compressed archive in any case so I do something like this.

git archive master | bzip2 >source-tree.tar.bz2

ZIP archive:

git archive --format zip --output /full/path/to/zipfile.zip master 

git help archive for more details, it's quite flexible.


Be aware that even though the archive will not contain the .git directory, it will, however, contain other hidden git-specific files like .gitignore, .gitattributes, etc. If you don't want them in the archive, make sure you use the export-ignore attribute in a .gitattributes file and commit this before doing your archive. Read more...


Note: If you are interested in exporting the index, the command is

git checkout-index -a -f --prefix=/destination/path/

(See Greg's answer for more details)

I found out what option 2 means. From a repository, you can do:

git checkout-index -a -f --prefix=/destination/path/

The slash at the end of the path is important, otherwise it will result in the files being in /destination with a prefix of 'path'.

Since in a normal situation the index contains the contents of the repository, there is nothing special to do to "read the desired tree into the index". It's already there.

The -a flag is required to check out all files in the index (I'm not sure what it means to omit this flag in this situation, since it doesn't do what I want). The -f flag forces overwriting any existing files in the output, which this command doesn't normally do.

This appears to be the sort of "git export" I was looking for.

git archive also works with remote repository.

git archive --format=tar \
--remote=ssh://remote_server/remote_repository master | tar -xf -

To export particular path inside the repo add as many paths as you wish as last argument to git, e.g.:

git archive --format=tar \
--remote=ssh://remote_server/remote_repository master path1/ path2/ | tar -xv

From the Git Manual:

Using git-checkout-index to "export an entire tree"

The prefix ability basically makes it trivial to use git-checkout-index as an "export as tree" function. Just read the desired tree into the index, and do:

$ git checkout-index --prefix=git-export-dir/ -a

I've written a simple wrapper around git-checkout-index that you can use like this:

git export ~/the/destination/dir

If the destination directory already exists, you'll need to add -f or --force.

Installation is simple; just drop the script somewhere in your PATH, and make sure it's executable.

The github repository for git-export

It appears that this is less of an issue with Git than SVN. Git only puts a .git folder in the repository root, whereas SVN puts a .svn folder in every subdirectory. So "svn export" avoids recursive command-line magic, whereas with Git recursion is not necessary.

This will copy all contents, minus the .dot files. I use this to export git cloned projects into my web app's git repo without the .git stuff.

cp -R ./path-to-git-repo /path/to/destination/

Plain old bash works just great :)

My preference would actually be to have a dist target in your Makefile (or other build system) that exports a distributable archive of your code (.tar.bz2, .zip, .jar, or whatever is appropriate). If you happen to be using GNU autotools or Perl's MakeMaker systems, I think this exists for you automatically. If not, I highly recommend adding it.

ETA (2012-09-06): Wow, harsh downvotes. I still believe it is better to build your distributions with your build tools rather than your source code control tool. I believe in building artifacts with build tools. In my current job, our main product is built with an ant target. We are in the midst of switching source code control systems, and the presence of this ant target means one less hassle in migration.

a git export to a zip archive while adding a prefix (e.g. directory name):

git archive master --prefix=directoryWithinZip/  --format=zip -o out.zip

I needed this for a deploy script and I couldn't use any of the above mentioned approaches. Instead I figured out a different solution:

#!/bin/sh
[ $# -eq 2 ] || echo "USAGE $0 REPOSITORY DESTINATION" && exit 1
REPOSITORY=$1
DESTINATION=$2
TMPNAME="/tmp/$(basename $REPOSITORY).$$"
git clone $REPOSITORY $TMPNAME
rm -rf $TMPNAME/.git
mkdir -p $DESTINATION
cp -r $TMPNAME/* $DESTINATION
rm -rf $TMPNAME

Doing it the easy way, this is a function for .bash_profile, it directly unzips the archive on current location, configure first your usual [url:path]. NOTE: With this function you avoid the clone operation, it gets directly from the remote repo.

gitss() {
    URL=[url:path]

    TMPFILE="`/bin/tempfile`"
    if [ "$1" = "" ]; then
        echo -e "Use: gitss repo [tree/commit]\n"
        return
    fi
    if [ "$2" = "" ]; then
        TREEISH="HEAD"
    else
        TREEISH="$2"
    fi
    echo "Getting $1/$TREEISH..."
    git archive --format=zip --remote=$URL/$1 $TREEISH > $TMPFILE && unzip $TMPFILE && echo -e "\nDone\n"
    rm $TMPFILE
}

Alias for .gitconfig, same configuration required (TAKE CARE executing the command inside .git projects, it ALWAYS jumps to the base dir previously as said here, until this is fixed I personally prefer the function

ss = !env GIT_TMPFILE="`/bin/tempfile`" sh -c 'git archive --format=zip --remote=[url:path]/$1 $2 \ > $GIT_TMPFILE && unzip $GIT_TMPFILE && rm $GIT_TMPFILE' -
Related