2544

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.

Vlad L.
  • 154
  • 1
  • 9
Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
  • 1
    @rnrTom: See Somov's answer. (there's nothing "compressed" in a tar archive). – etarion May 23 '12 at 15:59
  • 33
    @mrTom `git archive --format zip --output "output.zip" master -0` will give you an uncompressed archive (-0 is the flag for uncompressed). http://git-scm.com/docs/git-archive. –  Sep 05 '12 at 18:47
  • 9
    I concur with @mrTom, and I don't think whether the archive is compressed or uncompressed is the main issue. With SVN, I can `export` a 250 kB subdirectory directly from remote repository (which could otherwise be 200 MB in size, excluding revisions) - and I will only hit the network for 250 kB (or so) download transfer. With `git`, `archive` has to be enabled on server (so I can't try it) - `clone --depth 1` from server may still retrieve a repo of say 25 MB, where the `.git` subfolder alone takes 15MB. Therefore, I'd still say answer is "no". – sdaau Jan 28 '13 at 14:59
  • @mrTom the answer is in fact **YES** See the OP's answer - the command is `git checkout-index` – nocache Nov 20 '13 at 08:55
  • 2
    Here is a nice and simple way: `git archive -o latest.zip HEAD` – Evgeni Sergeev Jul 23 '18 at 18:43
  • 7
    I've been using this question as the man-page for "git export" for years now, fyi. – orion elenzil May 06 '21 at 16:14

31 Answers31

2563

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)


Here's a real-world example using libchrony on Linux:

mkdir $HOME/dev
cd $HOME/dev
pushd /tmp
git clone https://gitlab.com/chrony/libchrony.git
cd libchrony
BRANCH=$(git rev-parse --abbrev-ref HEAD)
git archive -o ../libchrony.zip --prefix="libchrony/" $BRANCH
popd
unzip /tmp/libchrony.zip

Those commands produce a zip file and extract it into $HOME/dev/libchrony. We can peek into the archive using:

$ unzip -v /tmp/libchrony
Archive:  /tmp/libchrony.zip
e0a3807f770b56f6b0e9833254baa7c4fc13564b
 Length   Method    Size  Cmpr    Date    Time   CRC-32   Name
--------  ------  ------- ---- ---------- ----- --------  ----
       0  Stored        0   0% 2023-07-20 09:37 00000000  libchrony/
      49  Defl:N       47   4% 2023-07-20 09:37 37c3f2e2  libchrony/.gitignore
   26530  Defl:N     9350  65% 2023-07-20 09:37 5622583e  libchrony/COPYING
     961  Defl:N      467  51% 2023-07-20 09:37 da9221e3  libchrony/Makefile
     475  Defl:N      304  36% 2023-07-20 09:37 cae27f70  libchrony/README.adoc
    3313  Defl:N     1119  66% 2023-07-20 09:37 37eb110f  libchrony/chrony.h
    7673  Defl:N     2261  71% 2023-07-20 09:37 5d455a52  libchrony/client.c
    6190  Defl:N     2093  66% 2023-07-20 09:37 7ea9d81b  libchrony/example-reports.c
   16348  Defl:N     3855  76% 2023-07-20 09:37 e82f5fe3  libchrony/message.c
    2946  Defl:N     1099  63% 2023-07-20 09:37 945ee82b  libchrony/message.h
--------          -------  ---                            -------
   64485            20595  68%                            10 files
Dave Jarvis
  • 30,436
  • 41
  • 178
  • 315
CB Bailey
  • 755,051
  • 104
  • 632
  • 656
  • 209
    ZIP archive: `git archive --format zip --output /full/path master` – Vadim Apr 23 '10 at 09:19
  • 227
    Be aware that the archive will not contain the .git directory, but will contain other hidden git-specific files like .gitignore, .gitattributes, etc. So if you don't want them, make sure you use the export-ignore attribute in a .gitattributes file and commit this before doing your archive. See http://feeding.cloud.geek.nz/2010/02/excluding-files-from-git-archive.html – mj1531 Jul 06 '10 at 14:55
  • 58
    To follow up on Streams' note: you can add a '--prefix=something/' string into the command to control the directory name that will be packed inside the zip. For example, if you use `git archive --format zip --output /path/to/file.zip --prefix=newdir/ master` the output will be called 'file.zip' but when you unpack it, the top level directory will be 'newdir'. (If you omit the --prefix attribute, the top level dir would be 'file'.) – Alan W. Smith Sep 30 '10 at 03:31
  • 1
    and to follow up on @anotherAlan's note: the --prefix also works for tar archives -- and in either case, be sure to include the trailing /, if you're expecting a directory! – lindes Feb 06 '11 at 09:27
  • 93
    The easiest way: `git archive -o latest.zip HEAD` It create a Zip archive that contains the contents of the latest commit on the current branch. Note that the output format is inferred by the extension of the output file. – nacho4d Jun 01 '11 at 10:47
  • 37
    It does not support git submodules :( – umpirsky Jul 21 '11 at 07:59
  • You can get a tar by doing "--format=tar --output=foo.tar", you don't have to pipe it through tar. In fact, according to git help archive, tar format is the default, so you don't even need to specify that. – Liam Oct 30 '11 at 15:43
  • 3
    @Liam: Yes, the default format is `tar` which is why you *can* pipe the output straight to `tar -x`. – CB Bailey Oct 31 '11 at 08:11
  • For single files, it may be useful to pipe to stdout. e.g.: `git archive master some/file/name | tar -xO` (capital `O`, not zero). – phils Feb 07 '12 at 04:36
  • @Charles Bailey: I wonder why you would choose this solution over clone + remove git-folders. Is there a downside to it? – Raffael Mar 23 '12 at 13:22
  • 1
    @Яaffael1984: one reason to do this over the clone & remove git-folders method is if you want to export the git repo into/on top of an existing directory. `git clone` isn't happy about cloning into a non-empty directory. – Michael Burr May 04 '12 at 20:44
  • 2
    Although this does work and is very simple, there is a big drawback which I have just come across. This will export all the files from the tree and set their timestamp to the time of the last git commit, instead of each file retaining it's own timestamp, it will be overwritten with the last committed file's stamp. This will then cause a problem for something like rsync if you were firing off your files to a remote server, all of the files will be transferred. – jmoz Jul 03 '12 at 00:30
  • @jmoz: That's not a good use case for `git archive`. If you are interested in the timestamps of files in the working tree then a plain `tar`, `cp -pr` or local `rsync` is more appropriate. – CB Bailey Jul 03 '12 at 05:35
  • 1
    `git archive --format zip --output "output.zip" master -0` will give you an uncompressed archive (-0 is the flag for uncompressed). http://git-scm.com/docs/git-archive –  Aug 27 '12 at 16:50
  • 6
    Nice, I added the zip option as an alias in `~/.gitconfig` `[alias] zip = archive --format zip --output`. Usage: `git zip ~/Desktop/app.zip master` – schmunk Apr 03 '13 at 17:33
  • Is it possible to do something like this? `git archive -o ../{$GIT_DIR}.zip HEAD` I want to generate the zip file name dynamically based on the repo name or directory name. – Evan Mattson Feb 19 '14 at 14:36
  • 1
    The `--remote` option lets you create an archive without needing to clone it first. – onionjake Aug 12 '14 at 15:27
  • Is there a way to archive only one folder of the project? – Jcao02 Oct 13 '14 at 13:36
  • 1
    Note that .tar = no compression (less overhead) .tar.gz or bz2 means smaller files, but huge CPU usage. – takeshin Aug 03 '15 at 13:15
  • "master" put me off. You really meant the name of the folder to export right? I replaced that with a dot. – Jonny Aug 04 '16 at 01:29
  • 1
    Note that `git archive` is not a solution of you're using `git-lfs`. – Paulo Moura Apr 07 '17 at 23:25
  • 1
    git archive does not pick up files that are not committed. I was trying to use it to make a backup of all of the files including files I was editing and it was not picking up the latest (edited) files. This is as designed I'm sure. I now need to find a way to get a list of every file in the HEAD and use cpio to copy the files so I pick up the files being edited. The command I need is git ls-tree -r master --name-only | cpio -pdmv /some/dest – PatS Mar 03 '18 at 05:11
  • As of today in most repost probably you would replace `master` with `main` – Marco Torchiano Jul 23 '22 at 16:07
  • Unfortunately this seems to mess with the timestamps of unchanged files too, which can cause unnecessary rebuilds if you attempt to build... – user541686 Dec 20 '22 at 07:35
346

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.

etarion
  • 16,935
  • 4
  • 43
  • 66
Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
  • 77
    ...and DON'T FORGET THE SLASH AT THE END, or you won't have the desired effect ;) – conny Apr 08 '09 at 20:48
  • Isn't the index just a name for the "staging" area? How's it that the tree is already there? I thought you can only add stuff manually to it with `git add` – hasen Aug 31 '09 at 06:12
  • 1
    The `git add` command changes content in the index, so whatever `git status` shows as "to be committed" is the *differences* between HEAD and the contents of the index. – Greg Hewgill Aug 31 '09 at 06:30
  • seems that this is equivalent to `git archive HEAD` – takeshin May 05 '10 at 20:33
  • 7
    @conny: read your comment, forgot about it and ran the command without a trailing slash. tip: follow conny's advice -.- – Markus Hedlund Jun 24 '10 at 16:55
  • I like to remove other [cruft][1] too. For instance, I remove the `.gitignore` file as well, since it probably won't be relevant to any future maintainers of the software. [1]: http://en.wikipedia.org/wiki/Cruft – mkelley33 Feb 20 '11 at 14:59
  • And apparently I didn't get the syntax correct in my comment above for linking to the term cruft on wikipedia. Sorry! – mkelley33 Feb 20 '11 at 15:07
  • 35
    +1 to conny's advice. Also, don't try to create '~/dest/', as this creates a directory called '~' in your working directory, rather than what you really wanted. Guess what happens when you mindlessly type rm -rf ~ – Kyle Heironimus Apr 18 '11 at 16:38
  • 1
    The reason for the '-a' flag is covered in the man page for git-checkout-index. It argues that git-checkout-index will frequently be accepting arguments, and sometimes no arguments means don't do anything. If you run `man git-checkout-index` and search for "Intuitive is not the goal here." you'll see the bit I'm talking about. – Daniel Kessler Jul 25 '12 at 20:11
  • This method won't accept the tag or any other reference to the revision. Admittedly, the need to specify the tag was more important for CVS, as somebody else could have changed the remote repository. Another caveat is that the above method would export the files from the index. That is, if something is staged to be committed, it will be exported. – proski Jun 11 '15 at 00:54
  • 5
    @KyleHeironimus - your warning about using '~/dest/` is true iff you use quotes around your prefix path which tells the shell not to perform tilde expansion. A dir called `~` (not `'~'` !) will be created in your working dir. There is nothing special about `git checkout-index` in this regard: The same is true of `mkdir '~/dest'` (*don't do that!*). Yet another good reason to avoid file names that need quoting (e.g. that have a space in them) :-) – Matt Wallis Sep 02 '15 at 09:18
  • I have not seen others mention that if the directory /destination/path/ does not already exist, you need to create it, as git will not do that for you and will silently do nothing unless it finds it. – Rich Mar 31 '16 at 00:03
  • 1
    @MattWallis: Using this approach in Windows with "Git Bash" as included in client available at git-scm.com tilde expansion doesn't work, though bash is using tilde as part of prompt. That isn't related to using quotes around pathname. at Rich: In this very same scenario any output folder was created on demand. – Thomas Urban Apr 06 '16 at 12:01
  • 1
    @takeshin It's not exactly the same as `git archive HEAD`: `git checkout-index ...` copies all files _from the index_, while `archive` archives the files _from the local repository_. They'll coincidentally do the same if the index is empty. – Alberto Jun 02 '16 at 08:34
  • Will `git checkout-index` try to re-use existing files similarly to what a normal `checkout` does (e.g. avoiding copying files with expected timestamp and size)? – ceztko Dec 05 '19 at 22:54
  • For those wondering [what the index is](//git-scm.com/docs/git-add): The `.git/index` file _“holds a snapshot of the content of the working tree ... taken as the contents of the next commit. ... You must use the add command to add any new or modified files to the index.”_ One entry in it basically [consists](//git-scm.com/docs/index-format) of filename, checksum and some flags. – cachius May 09 '22 at 09:27
  • This doesn't work for me: the files do not have the correct timestamps. – vinc17 Jun 01 '22 at 14:48
270

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
Alexander Somov
  • 2,798
  • 1
  • 15
  • 5
  • 6
    This one is the option I like best. It has the additional benefit that it also works on bare repositories. – innaM Aug 31 '09 at 14:34
  • 7
    Am improved version is: `git archive --format=tar --prefix=PROJECT_NAME/ --remote=USER@SERVER:PROJECT_NAME.git master | tar -xf -` (ensures your archive is in a folder) – Nick Dec 15 '11 at 15:57
  • For some reason this doesn't seem to work on github for me. So i wrote a small wrapper in zsh: https://gist.github.com/1895274 – Sebastian Stumpf Feb 23 '12 at 21:59
  • this worked for me +1. How do I specify a target foler based on this example? – Robbo_UK Jun 06 '12 at 10:09
  • 1
    Managed to answer my own question export to a specified directory `git archive --format=tar --remote=ssh://remote_server/remote_repository master | (cd /path/to/dir/ && tar -xf -)` – Robbo_UK Jun 06 '12 at 10:50
  • 11
    **Note**: server must enable this feature. – Jakub Narębski Dec 13 '12 at 11:19
  • if you're used to svn export http://repository.com/etc, this is the closest option. – jsd Feb 20 '13 at 22:58
  • 14
    I tried : `git archive --format=zip --output foo.zip --remote=https://github.com/xxx.git master` And got fatal: Operation not supported by protocol. Unexpected end of command stream. – andyf Jul 19 '13 at 08:04
  • @andyf only git and smart http protocol support it. If simple http on server, then it does not make it. http://stackoverflow.com/a/11258694/520567 – akostadinov Jul 22 '13 at 16:56
  • 10
    @andyf GitHub has its own way: `curl -L https://api.github.com/repos/VENDOR/PROJECT/tarball | tar xzf -` per [docs](https://developer.github.com/v3/repos/contents/#get-archive-link) – bishop Jul 31 '14 at 13:51
  • @bishop GitHub supports (but doesn't advertise) the git protocol too. Just replace https:// with git:// in the URL. – Neil Mayhew Jan 13 '16 at 18:43
  • 3
    @NeilMayhew It doesn't work for me, I get `fatal: The remote end hung up unexpectedly`. Tried on two different servers with jQuery github repo. – Anthony Hatzopoulos Jan 23 '16 at 23:54
  • To compress it over the wire you can also use `git archive --format=tar.gz \ --remote=ssh://remote_server/remote_repository master path1/ path2/ | tar -xzv` – AmanicA Aug 18 '23 at 02:21
79

enter image description here

A special case answer if the repository is hosted on GitHub.

Just use svn export.

As far as I know Github does not allow archive --remote. Although GitHub is svn compatible and they do have all git repos svn accessible so you could just use svn export like you normally would with a few adjustments to your GitHub url.

For example to export an entire repository, notice how trunk in the URL replaces master (or whatever the project's HEAD branch is set to):

svn export https://github.com/username/repo-name/trunk/

And you can export a single file or even a certain path or folder:

svn export https://github.com/username/repo-name/trunk/src/lib/folder

Example with jQuery JavaScript Library

The HEAD branch or master branch will be available using trunk:

svn ls https://github.com/jquery/jquery/trunk

The non-HEAD branches will be accessible under /branches/:

svn ls https://github.com/jquery/jquery/branches/2.1-stable

All tags under /tags/ in the same fashion:

svn ls https://github.com/jquery/jquery/tags/2.1.3
Anthony Hatzopoulos
  • 10,437
  • 2
  • 40
  • 57
  • 2
    `git archive` works fine with GitHub, as long as you use the git protocol, Just replace `https://` with `git://` in the URL. I don't know why GitHub doesn't advertise this hidden feature. – Neil Mayhew Jan 13 '16 at 18:48
  • 1
    @NeilMayhew It doesn't work for me, I get `fatal: The remote end hung up unexpectedly`. Tried on two different servers with jQuery github repo. – Anthony Hatzopoulos Jan 23 '16 at 23:55
  • 1
    You're right. I'd forgotten that I was using `git config url..insteadOf` to cache the remote repository. I was therefore using a `file://` URL in reality. I doubt that `git archive` could ever work with `git://` URLs since it needs to be able to run `git-upload-archive` at the remote end. It should be possible using the `ssh` protocol, except that github doesn't allow it (`Invalid command: 'git-upload-archive'`). – Neil Mayhew Jan 25 '16 at 14:49
  • Any way to use a local server tool behaving like github if I want to do it on internally hosted git repositories ? – kriss Nov 14 '16 at 14:47
  • 3
    upvoted -- it's completely bizarre that Git doesn't have this feature and we have to resort to svn – Jason S May 31 '17 at 16:23
  • I wonder how to use `svn export` with 2FA or with API token on github. – Ondrej Burkert Mar 13 '18 at 09:02
  • Another option for fetching from GitHub is to use the REST API, see this Python script: https://gist.github.com/LewisGaul/65aa7d4ff5a20abb1c8ea02cd0948486 – Siwel Dec 01 '20 at 21:40
44

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

jperras
  • 702
  • 1
  • 5
  • 9
  • 23
    I think the confusion is the phrase "read the desired tree into the index". – davetron5000 Oct 02 '08 at 02:30
  • 4
    If you want to export directory foo in branch bar, then this would be `git read-tree bar:foo` And then `git checkout-index --prefix=export_dir/ -a` after that maybe you should do `git update-index master` – Pascal Rosin Jun 08 '12 at 17:10
  • 2
    @JohnWeldon Does it require you to clone the repo first? If so, then I wouldn't accept it, since the whole point of "svn export" of a subdirectory is to directly get a copy of that subdirectory; if someone has a 1GB Git repo and all I want is a 10kB subdirectory, it's insane to require me to clone the whole thing. – Jason S May 31 '17 at 16:44
  • 6
    Also I'd echo @davetron5000 with the comment "read the desired tree into the index" which I have no idea what it means. – Jason S May 31 '17 at 16:44
40

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

Daniel Schierbeck
  • 1,942
  • 2
  • 17
  • 24
  • 15
    This wrapper is not platform-agnostic; it relies on /bin/sh. So if you're on Windows, this solution _probably_ won't work for you. – shovavnik Jul 18 '13 at 21:14
  • 20
    Uhh, this script is 57 lines of documentation, whitespace, setup, argument parsing, and only one line which actually does something... – Vladimir Panteleev May 13 '15 at 16:23
36

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.

kostmo
  • 6,222
  • 4
  • 40
  • 51
  • 27
    As of SVN 1.7, there is also only one .svn folder: https://subversion.apache.org/docs/release-notes/1.7.html#single-db – kostmo Jul 25 '11 at 21:10
  • This won't get rid of any additional build files that svn export deletes. So this is definitely not the answer. – ygoe Apr 16 '14 at 08:32
30

The equivalent of

svn export . otherpath

inside an existing repo is

git archive branchname | (cd otherpath; tar x)

The equivalent of

svn export url otherpath

is

git archive --remote=url branchname | (cd otherpath; tar x)
aredridel
  • 1,532
  • 14
  • 19
  • 1
    thanks, this was what I was missing... also, to check the timestamps of the export (they will not be preserved as on the files), use `git archive --format=tar --prefix=junk/ HEAD | (tar -t -v --full-time -f -)` ... However, archiving with timestamps is not exactly trivial, so I posted an [example below](http://stackoverflow.com/questions/160608/how-to-do-a-git-export-like-svn-export/24760614#24760614). – sdaau Jul 15 '14 at 14:18
  • 2
    You can use the C option for tar instead of the subshell, like this: `git archive branchname | tar xC otherpath` – James Moore Nov 11 '16 at 19:34
  • Heads up that the `C` option to tar is GNU Tar only. – aredridel Oct 18 '17 at 14:30
25

If you're not excluding files with .gitattributes export-ignore then try git checkout

mkdir /path/to/checkout/
git --git-dir=/path/to/repo/.git --work-tree=/path/to/checkout/ checkout -f -q

-f
When checking out paths from the index, do not fail upon unmerged entries; instead, unmerged entries are ignored.

and

-q
Avoid verbose

Additionally you can get any Branch or Tag or from a specific Commit Revision like in SVN just adding the SHA1 (SHA1 in Git is the equivalent to the Revision Number in SVN)

mkdir /path/to/checkout/
git --git-dir=/path/to/repo/.git --work-tree=/path/to/checkout/ checkout 2ef2e1f2de5f3d4f5e87df7d8 -f -q -- ./

The /path/to/checkout/ must be empty, Git will not delete any file, but will overwrite files with the same name without any warning

UPDATE: To avoid the beheaded problem or to leave intact the working repository when using checkout for export with tags, branches or SHA1, you need to add -- ./ at the end

The double dash -- tells git that everything after the dashes are paths or files, and also in this case tells git checkout to not change the HEAD

Examples:

This command will get just the libs directory and also the readme.txt file from that exactly commit

git --git-dir=/path/to/repo/.git --work-tree=/path/to/checkout/ checkout fef2e1f2de5f3d4f5e87df7d8 -f -q -- ./libs ./docs/readme.txt

This will create(overwrite) my_file_2_behind_HEAD.txt two commits behind the head HEAD^2

git --git-dir=/path/to/repo/.git --work-tree=/path/to/checkout/ checkout HEAD^2 -f -q -- ./my_file_2_behind_HEAD.txt

To get the export of another branch

git --git-dir=/path/to/repo/.git --work-tree=/path/to/checkout/ checkout myotherbranch -f -q -- ./

Notice that ./ is relative to the root of the repository

Jeff Puckett
  • 37,464
  • 17
  • 118
  • 167
  • Actually, among numerous others and upvotes, this worked best for me, without any compressions, working fine with bare repositories (gitolite). – takeshin Oct 03 '14 at 14:00
  • 1
    Notice that the SHA1 checkout will create a "behead" problem in the repository – user5286776117878 Oct 04 '14 at 17:26
  • actualy @ITGabs, this does not download ".git" folder. So the downloaded folder is not a git repository, so it is not technically "beheaded" – Fabio Marreco May 03 '16 at 19:22
  • @FabioMarreco The behead issue is on the repository not in the exported/downloaded files, I am updating the answer for more details – user5286776117878 May 07 '16 at 07:04
  • 3
    This worked great for me. But at first I got "Not a git repository" error messages. Then I found that "/path/to/repo/" had to point to the .git folder. So this worked: --git-dir=/path/to/repo/.git – philburk May 10 '16 at 15:05
  • What if I want to export an earlier commit? First, checkout to earlier commit with `git checkout `, then your command `git --git-dir=/path/to/repo/ --work-tree=/path/to/checkout/ checkout -f -q`? – horse Mar 11 '17 at 06:19
  • The command worked well. The only drawback observed is that it is not possible to run `git checkout ...` in parallel. Running in parallel gives: `fatal: Unable to create '.../index.lock': File exists.`. Have to use `git clone --branch ... /path/to/repo/.git /path/to/checkout` instead: it works when running in parallel. – Pavel Shishpor Mar 30 '23 at 09:38
22

I use git-submodules extensively. This one works for me:

rsync -a ./FROM/ ./TO --exclude='.*'
slatvick
  • 1,207
  • 2
  • 16
  • 25
22

I have hit this page frequently when looking for a way to export a git repository. My answer to this question considers three properties that svn export has by design compared to git, since svn follows a centralized repository approach:

  • It minimizes the traffic to a remote repository location by not exporting all revisions

  • It does not include meta information in the export directory

  • Exporting a certain branch using svn is accomplished by specifying the appropriate path

      git clone --depth 1 --branch main git://git.somewhere destination_path
      rm -rf destination_path/.git
    

When building a certain release it is useful to clone a stable branch as for example --branch stable or --branch release/0.9.

Lars Schillingmann
  • 1,528
  • 1
  • 13
  • 15
  • This does not work if the destination exists and is non-empty. – Eponymous Jan 13 '15 at 18:20
  • 2
    **The One True Answer:** *it arises from the depths.* The `git archive | tar` approach is inapplicable to POSIX-incompatible shell environments (e.g., AppVeyor's CMD- or PowerShell-based CI), which is non-ideal. The `git checkout` approach modifies the index of the main working tree, which is awful. The `git checkout-index` approach requires the index of the main working tree to be modified beforehand, which is even awful-er. The traditional `git clone` approach clones the entirety of the repository's history before deleting that history, which is wasteful. This is the only sane solution left. – Cecil Curry Aug 10 '17 at 07:17
  • 1
    To export locally, note that the absolute path of the Git working tree to be cloned from should be prefixed by the `file://` protocol (e.g., `git clone --depth 1 --branch v3.14.15 file:///home/me/src_repo trg_repo`). Failing to do so will emit `"warning: --depth is ignored in local clones; use file:// instead."` and perform a standard rather than shallow clone, defeating the entire purpose of this answer. *Salud!* – Cecil Curry Aug 10 '17 at 07:24
  • `master` --> `main` as of today in most repos – Marco Torchiano Jul 23 '22 at 16:03
  • For the celebrations to the tenth anniversary of this answer I renamed the branch name. – Lars Schillingmann Jul 24 '22 at 19:45
16

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 :)

Harmon
  • 4,219
  • 2
  • 19
  • 16
13

As simple as clone then delete the .git folder:

git clone url_of_your_repo path_to_export && rm -rf path_to_export/.git

teleme.io
  • 815
  • 3
  • 10
  • 21
  • 5
    Honestly - this answer, which is also #1 in the question - is what you're going to be doing 99% of the time. Most of these answers are crazy overcomplicated. – Geoff Nixon Mar 07 '14 at 14:28
  • −1. First, this way is already mentioned in the question as one that doesn't satisfy the question author. Second, in this way the whole history will be downloaded, which can be much larger than the useful part. Please at least include `--depth 1`. Third, even if this answer is improved, it adds nothing to [the Lars Schillingmann's answer](/a/8963061). – Sasha Feb 25 '21 at 11:36
12

For GitHub users, the git archive --remote method won't work directly, as the export URL is ephemeral. You must ask GitHub for the URL, then download that URL. curl makes that easy:

curl -L https://api.github.com/repos/VENDOR/PROJECT/tarball | tar xzf -

This will give you the exported code in a local directory. Example:

$ curl -L https://api.github.com/repos/jpic/bashworks/tarball | tar xzf -
$ ls jpic-bashworks-34f4441/
break  conf  docs  hack  LICENSE  mlog  module  mpd  mtests  os  README.rst  remote  todo  vcs  vps  wepcrack

Edit
If you want the code put into a specific, existing directory (rather than the random one from github):

curl -L https://api.github.com/repos/VENDOR/PROJECT/tarball | \
tar xzC /path/you/want --strip 1
bishop
  • 37,830
  • 11
  • 104
  • 139
11

Yes, this is a clean and neat command to archive your code without any git inclusion in the archive and is good to pass around without worrying about any git commit history.

git archive --format zip --output /full/path/to/zipfile.zip master 
Community
  • 1
  • 1
zeeawan
  • 6,667
  • 2
  • 50
  • 56
  • This is great, just need to remove gitignore after and it is done and ready to share. – Sogger Jan 28 '16 at 23:53
  • Removing .gitgnore etc. is mentioned in accepted answer comments: use .gitattributes file, see https://feeding.cloud.geek.nz/posts/excluding-files-from-git-archive/ – Sogger Jan 28 '16 at 23:59
10

I just want to point out that in the case that you are

  1. exporting a sub folder of the repository (that's how I used to use SVN export feature)
  2. are OK with copying everything from that folder to the deployment destination
  3. and since you already have a copy of the entire repository in place.

Then you can just use cp foo [destination] instead of the mentioned git-archive master foo | -x -C [destination].

dkinzer
  • 32,179
  • 12
  • 66
  • 85
9

You can archive a remote repo at any commit as zip file.

git archive --format=zip --output=archive.zip --remote=USERNAME@HOSTNAME:PROJECTNAME.git HASHOFGITCOMMIT
orkoden
  • 18,946
  • 4
  • 59
  • 50
8

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.

skiphoppy
  • 97,646
  • 72
  • 174
  • 218
  • The project I had in mind isn't a code project; it happens to be more along the lines of a web site project. – Greg Hewgill Oct 02 '08 at 17:53
  • Doesn't address the question. – Andrew Ferrier Sep 18 '12 at 17:07
  • 1
    Yeah, such an answer may not fit everybody's needs, but the downvotes are bizarre. It _is_ a totally valid answer, and indeed, in many scenarios, the only correct answer. It makes the very valid point that thinking about this issue as a "vc tool issue" is often going down the wrong path entirely. – snogglethorpe Sep 09 '13 at 23:37
8

As I understand the question, it it more about downloading just certain state from the server, without history, and without data of other branches, rather than extracting a state from a local repository (as many anwsers here do).

That can be done like this:

git clone -b someBranch --depth 1 --single-branch git://somewhere.com/repo.git \
&& rm -rf repo/.git/
  • --single-branch is available since Git 1.7.10 (April 2012).
  • --depth is (was?) reportedly faulty, but for the case of an export, the mentioned issues should not matter.
Ondra Žižka
  • 43,948
  • 41
  • 217
  • 277
  • Note: I just noticed there are 2 pages of anwsers, I only looked at one before posting. There is one similar anwser with only `--depth`, which implies `--single-branch` unless `--no-single-branch` is given, which means this has probably the same effect. Not sure though, some expert may confirm? – Ondra Žižka Mar 12 '18 at 14:56
8

If you want something that works with submodules this might be worth a go.

Note:

  • MASTER_DIR = a checkout with your submodules checked out also
  • DEST_DIR = where this export will end up
  • If you have rsync, I think you'd be able to do the same thing with even less ball ache.

Assumptions:

  • You need to run this from the parent directory of MASTER_DIR ( i.e from MASTER_DIR cd .. )
  • DEST_DIR is assumed to have been created. This is pretty easy to modify to include the creation of a DEST_DIR if you wanted to

cd MASTER_DIR && tar -zcvf ../DEST_DIR/export.tar.gz --exclude='.git*' . && cd ../DEST_DIR/ && tar xvfz export.tar.gz && rm export.tar.gz

Rob Jensen
  • 81
  • 1
  • 1
7

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
DomTomCat
  • 8,189
  • 1
  • 49
  • 64
7

Bash-implementation of git-export.

I have segmented the .empty file creation and removal processes on their own function, with the purpose of re-using them in the 'git-archive' implementation (will be posted later on).

I have also added the '.gitattributes' file to the process in order to remove un-wanted files from the target export folder. Included verbosity to the process while making the 'git-export' function more efficient.

EMPTY_FILE=".empty";

function create_empty () {
## Processing path (target-dir):
    TRG_PATH="${1}";
## Component(s):
    EXCLUDE_DIR=".git";
echo -en "\nAdding '${EMPTY_FILE}' files to empty folder(s): ...";
    find ${TRG_PATH} -not -path "*/${EXCLUDE_DIR}/*" -type d -empty -exec touch {}/${EMPTY_FILE} \;
#echo "done.";
## Purging SRC/TRG_DIRs variable(s):
    unset TRG_PATH EMPTY_FILE EXCLUDE_DIR;
    return 0;
  }

declare -a GIT_EXCLUDE;
function load_exclude () {
    SRC_PATH="${1}";
    ITEMS=0; while read LINE; do
#      echo -e "Line [${ITEMS}]: '${LINE%%\ *}'";
      GIT_EXCLUDE[((ITEMS++))]=${LINE%%\ *};
    done < ${SRC_PATH}/.gitattributes;
    GIT_EXCLUDE[${ITEMS}]="${EMPTY_FILE}";
## Purging variable(s):
    unset SRC_PATH ITEMS;
    return 0;
  }

function purge_empty () {
## Processing path (Source/Target-dir):
    SRC_PATH="${1}";
    TRG_PATH="${2}";
echo -e "\nPurging Git-Specific component(s): ... ";
    find ${SRC_PATH} -type f -name ${EMPTY_FILE} -exec /bin/rm '{}' \;
    for xRULE in ${GIT_EXCLUDE[@]}; do
echo -en "    '${TRG_PATH}/{${xRULE}}' files ... ";
      find ${TRG_PATH} -type f -name "${xRULE}" -exec /bin/rm -rf '{}' \;
echo "done.'";
    done;
echo -e "done.\n"
## Purging SRC/TRG_PATHs variable(s):
    unset SRC_PATH; unset TRG_PATH;
    return 0;
  }

function git-export () {
    TRG_DIR="${1}"; SRC_DIR="${2}";
    if [ -z "${SRC_DIR}" ]; then SRC_DIR="${PWD}"; fi
    load_exclude "${SRC_DIR}";
## Dynamically added '.empty' files to the Git-Structure:
    create_empty "${SRC_DIR}";
    GIT_COMMIT="Including '${EMPTY_FILE}' files into Git-Index container."; #echo -e "\n${GIT_COMMIT}";
    git add .; git commit --quiet --all --verbose --message "${GIT_COMMIT}";
    if [ "${?}" -eq 0 ]; then echo " done."; fi
    /bin/rm -rf ${TRG_DIR} && mkdir -p "${TRG_DIR}";
echo -en "\nChecking-Out Index component(s): ... ";
    git checkout-index --prefix=${TRG_DIR}/ -q -f -a
## Reset: --mixed = reset HEAD and index:
    if [ "${?}" -eq 0 ]; then
echo "done."; echo -en "Resetting HEAD and Index: ... ";
        git reset --soft HEAD^;
        if [ "${?}" -eq 0 ]; then
echo "done.";
## Purging Git-specific components and '.empty' files from Target-Dir:
            purge_empty "${SRC_DIR}" "${TRG_DIR}"
          else echo "failed.";
        fi
## Archiving exported-content:
echo -en "Archiving Checked-Out component(s): ... ";
        if [ -f "${TRG_DIR}.tgz" ]; then /bin/rm ${TRG_DIR}.tgz; fi
        cd ${TRG_DIR} && tar -czf ${TRG_DIR}.tgz ./; cd ${SRC_DIR}
echo "done.";
## Listing *.tgz file attributes:
## Warning: Un-TAR this file to a specific directory:
        ls -al ${TRG_DIR}.tgz
      else echo "failed.";
    fi
## Purgin all references to Un-Staged File(s):
   git reset HEAD;
## Purging SRC/TRG_DIRs variable(s):
    unset SRC_DIR; unset TRG_DIR;
    echo "";
    return 0;
  }

Output:

$ git-export /tmp/rel-1.0.0

Adding '.empty' files to empty folder(s): ... done.

Checking-Out Index component(s): ... done.

Resetting HEAD and Index: ... done.

Purging Git-Specific component(s): ...

'/tmp/rel-1.0.0/{.buildpath}' files ... done.'

'/tmp/rel-1.0.0/{.project}' files ... done.'

'/tmp/rel-1.0.0/{.gitignore}' files ... done.'

'/tmp/rel-1.0.0/{.git}' files ... done.'

'/tmp/rel-1.0.0/{.gitattributes}' files ... done.'

'/tmp/rel-1.0.0/{*.mno}' files ... done.'

'/tmp/rel-1.0.0/{*~}' files ... done.'

'/tmp/rel-1.0.0/{.*~}' files ... done.'

'/tmp/rel-1.0.0/{*.swp}' files ... done.'

'/tmp/rel-1.0.0/{*.swo}' files ... done.'

'/tmp/rel-1.0.0/{.DS_Store}' files ... done.'

'/tmp/rel-1.0.0/{.settings}' files ... done.'

'/tmp/rel-1.0.0/{.empty}' files ... done.'

done.

Archiving Checked-Out component(s): ... done.

-rw-r--r-- 1 admin wheel 25445901 3 Nov 12:57 /tmp/rel-1.0.0.tgz

I have now incorporated the 'git archive' functionality into a single process that makes use of 'create_empty' function and other features.

function git-archive () {
    PREFIX="${1}"; ## sudo mkdir -p ${PREFIX}
    REPO_PATH="`echo "${2}"|awk -F: '{print $1}'`";
    RELEASE="`echo "${2}"|awk -F: '{print $2}'`";
    USER_PATH="${PWD}";
echo "$PREFIX $REPO_PATH $RELEASE $USER_PATH";
## Dynamically added '.empty' files to the Git-Structure:
    cd "${REPO_PATH}"; populate_empty .; echo -en "\n";
#    git archive --prefix=git-1.4.0/ -o git-1.4.0.tar.gz v1.4.0
# e.g.: git-archive /var/www/htdocs /repos/domain.name/website:rel-1.0.0 --explode
    OUTPUT_FILE="${USER_PATH}/${RELEASE}.tar.gz";
    git archive --verbose --prefix=${PREFIX}/ -o ${OUTPUT_FILE} ${RELEASE}
    cd "${USER_PATH}";
    if [[ "${3}" =~ [--explode] ]]; then
      if [ -d "./${RELEASE}" ]; then /bin/rm -rf "./${RELEASE}"; fi
      mkdir -p ./${RELEASE}; tar -xzf "${OUTPUT_FILE}" -C ./${RELEASE}
    fi
## Purging SRC/TRG_DIRs variable(s):
    unset PREFIX REPO_PATH RELEASE USER_PATH OUTPUT_FILE;
    return 0;
  }
tocororo
  • 61
  • 2
  • 2
6

This will copy the files in a range of commits (C to G) to a tar file. Note: this will only get the files commited. Not the entire repository. Slightly modified from Here

Example Commit History

A --> B --> C --> D --> E --> F --> G --> H --> I

git diff-tree -r --no-commit-id --name-only --diff-filter=ACMRT C~..G | xargs tar -rf myTarFile.tar

git-diff-tree Manual Page

-r --> recurse into sub-trees

--no-commit-id --> git diff-tree outputs a line with the commit ID when applicable. This flag suppressed the commit ID output.

--name-only --> Show only names of changed files.

--diff-filter=ACMRT --> Select only these files. See here for full list of files

C..G --> Files in this range of commits

C~ --> Include files from Commit C. Not just files since Commit C.

| xargs tar -rf myTarFile --> outputs to tar

Community
  • 1
  • 1
Fuyu Persimmon
  • 483
  • 6
  • 13
6

By far the easiest way i've seen to do it (and works on windows as well) is git bundle:

git bundle create /some/bundle/path.bundle --all

See this answer for more details: How can I copy my git repository from my windows machine to a linux machine via usb drive?

Community
  • 1
  • 1
B T
  • 57,525
  • 34
  • 189
  • 207
  • 1
    `git bundle` includes the `.git` folder, which is what the OP does not want; `git archive` seems the more appropriate way – ssc Feb 06 '16 at 13:45
  • Where is documentation on the `--all` switch? – Garret Wilson Nov 05 '17 at 14:30
  • @GarretWilson it's weird. --all here isn't an option to git bundle create, even though it sure looks like it. --all is a legal value to pass as git-rev-list, see https://git-scm.com/docs/git-rev-list. – James Moore Dec 02 '20 at 16:14
4

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
troelskn
  • 115,121
  • 27
  • 131
  • 155
  • What was the issue with either a read-tree/checkout-index or archive solution? As far as I can tell you've done the equivalent of something like `mkdir -p "$2" && git --git-dir="$1" archive HEAD | tar -x -C "$2"` but somewhat longer winded. – CB Bailey Jul 17 '09 at 10:16
  • 1
    I couldn't get read-tree to work from a remote repository, and the archive solution doesn't work with github. – troelskn Jul 17 '09 at 14:34
  • Yes with archive get a Invalid command: 'git-upload-archive '... error and i dont have core.gitProxy config option and the GIT_PROXY_COMMAND environment variable set – tgkprog Nov 26 '14 at 12:05
4

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' -
RkG
  • 49
  • 1
  • 2
4

I have another solution that works fine if you have a local copy of the repository on the machine where you would like to create the export. In this case move to this repository directory, and enter this command:

GIT_WORK_TREE=outputdirectory git checkout -f

This is particularly useful if you manage a website with a git repository and would like to checkout a clean version in /var/www/. In this case, add thiscommand in a .git/hooks/post-receive script (hooks/post-receive on a bare repository, which is more suitable in this situation)

Tom
  • 834
  • 1
  • 14
  • 24
3

I think @Aredridel's post was closest, but there's a bit more to that - so I will add this here; the thing is, in svn, if you're in a subfolder of a repo, and you do:

/media/disk/repo_svn/subdir$ svn export . /media/disk2/repo_svn_B/subdir

then svn will export all files that are under revision control (they could have also freshly Added; or Modified status) - and if you have other "junk" in that directory (and I'm not counting .svn subfolders here, but visible stuff like .o files), it will not be exported; only those files registered by the SVN repo will be exported. For me, one nice thing is that this export also includes files with local changes that have not been committed yet; and another nice thing is that the timestamps of the exported files are the same as the original ones. Or, as svn help export puts it:

  1. Exports a clean directory tree from the working copy specified by PATH1, at revision REV if it is given, otherwise at WORKING, into PATH2. ... If REV is not specified, all local changes will be preserved. Files not under version control will not be copied.

To realize that git will not preserve the timestamps, compare the output of these commands (in a subfolder of a git repo of your choice):

/media/disk/git_svn/subdir$ ls -la .

... and:

/media/disk/git_svn/subdir$ git archive --format=tar --prefix=junk/ HEAD | (tar -t -v --full-time -f -)

... and I, in any case, notice that git archive causes all the timestamps of the archived file to be the same! git help archive says:

git archive behaves differently when given a tree ID versus when given a commit ID or tag ID. In the first case the current time is used as the modification time of each file in the archive. In the latter case the commit time as recorded in the referenced commit object is used instead.

... but apparently both cases set the "modification time of each file"; thereby not preserving the actual timestamps of those files!

So, in order to also preserve the timestamps, here is a bash script, which is actually a "one-liner", albeit somewhat complicated - so below it is posted in multiple lines:

/media/disk/git_svn/subdir$ git archive --format=tar master | (tar tf -) | (\
  DEST="/media/diskC/tmp/subdirB"; \
  CWD="$PWD"; \
  while read line; do \
    DN=$(dirname "$line"); BN=$(basename "$line"); \
    SRD="$CWD"; TGD="$DEST"; \
    if [ "$DN" != "." ]; then \
      SRD="$SRD/$DN" ; TGD="$TGD/$DN" ; \
      if [ ! -d "$TGD" ] ; then \
        CMD="mkdir \"$TGD\"; touch -r \"$SRD\" \"$TGD\""; \
        echo "$CMD"; \
        eval "$CMD"; \
      fi; \
    fi; \
    CMD="cp -a \"$SRD/$BN\" \"$TGD/\""; \
    echo "$CMD"; \
    eval "$CMD"; \
    done \
)

Note that it is assumed that you're exporting the contents in "current" directory (above, /media/disk/git_svn/subdir) - and the destination you're exporting into is somewhat inconveniently placed, but it is in DEST environment variable. Note that with this script; you must create the DEST directory manually yourself, before running the above script.

After the script is ran, you should be able to compare:

ls -la /media/disk/git_svn/subdir
ls -la /media/diskC/tmp/subdirB   # DEST

... and hopefully see the same timestamps (for those files that were under version control).

Hope this helps someone,
Cheers!

Community
  • 1
  • 1
sdaau
  • 36,975
  • 46
  • 198
  • 278
2

If you need submodules as well, this should do the trick: https://github.com/meitar/git-archive-all.sh/wiki

Brandon
  • 953
  • 10
  • 12
1

i have the following utility function in my .bashrc file: it creates an archive of the current branch in a git repository.

function garchive()
{
  if [[ "x$1" == "x-h" || "x$1" == "x" ]]; then
    cat <<EOF
Usage: garchive <archive-name>
create zip archive of the current branch into <archive-name>
EOF
  else
    local oname=$1
    set -x
    local bname=$(git branch | grep -F "*" | sed -e 's#^*##')
    git archive --format zip --output ${oname} ${bname}
    set +x
  fi
}
MichaelMoser
  • 3,172
  • 1
  • 26
  • 26
1

The option 1 sounds not too efficient. What if there is no space in the client to do a clone and then remove the .git folder?

Today I found myself trying to do this, where the client is a Raspberry Pi with almost no space left. Furthermore, I also want to exclude some heavy folder from the repository.

Option 2 and others answers here do not help in this scenario. Neither git archive (because require to commit a .gitattributes file, and I don't want to save this exclusion in the repository).

Here I share my solution, similar to option 3, but without the need of git clone:

tmp=`mktemp`
git ls-tree --name-only -r HEAD > $tmp
rsync -avz --files-from=$tmp --exclude='fonts/*' . raspberry:

Changing the rsync line for an equivalent line for compress will also work as a git archive but with a sort of exclusion option (as is asked here).

alexis
  • 410
  • 4
  • 18