23

Git has the very handy archive command which allows me to make a copy of a particular commit in a .zip archive like so:

git archive -o ../latest.zip some-commit

This will contain the entire working tree for that commit. Usually I just need the changed files since a previous release. Currently I use this to get those files into a zip:

git diff --name-only previous-commit latest-commit | zip ../changes.zip -@

This will however zip files from my working copy, which may have uncommitted changes. Is there some way to get only the changed files as they were committed directly into a zip?

Marnix van Valen
  • 13,265
  • 4
  • 47
  • 74

6 Answers6

47

git archive will accept paths as arguments. All you should need to do is:

git archive -o ../latest.zip some-commit $(git diff --name-only earlier-commit some-commit)

or if you have files with spaces (or other special characters) in them, use xargs:

git diff --name-only earlier-commit some-commit | xargs -d'\n' git archive -o ../latest.zip some-commit

If you don't have xargs properly installed, you could cook up an alternative:

#!/bin/bash

IFS=$'\n'
files=($(git diff --name-only earlier-commit some-commit))

git archive -o ../latest.zip some-commit "${files[@]}"

Written as a shell script, but should work as a one-liner too. Alternatively, change earlier-commit, some-commit, and ../latest.zip to $1 $2 and $3 and you've got yourself a reusable script.

Cascabel
  • 479,068
  • 72
  • 370
  • 318
  • 6
    Beware that extracting such an archive will not delete any files. This may cause hard to diagnose problems later on. – jilles Aug 06 '10 at 16:30
  • @jilles: Very true. This is why patches are great. @Marnix van Valen: You could perhaps just generate the patch then wrap it with a script (you could even use a heredoc to keep it all in one file) - then it'd be both usable and, well, correct. – Cascabel Aug 06 '10 at 17:42
  • What I usually do is include a list of all added and removed files with the release. The customer will then manually add or remove the files to/from source control on his end. @Jefromi Good idea. That would completely remove any human errors. I'll give it a shot. – Marnix van Valen Aug 09 '10 at 10:05
  • @Jefromi One problem with the second command; it doesn't work on windows. The xargs version that comes with the Windows git installer doesn't support the -d option. Nor does any of the other xargs builds for windows I could find. Any idea's for an alternative? – Marnix van Valen Aug 09 '10 at 10:43
  • @Marnix van Valen: Then I'd do it within bash. See the answer. – Cascabel Aug 09 '10 at 11:43
  • There is a working solution for file names with spaces here: http://stackoverflow.com/questions/7226009/git-how-to-create-archive-with-files-that-have-been-changed/7226334#comment12246899_7226334 – Neville Cook Mar 10 '12 at 12:56
  • @marnix-van-valen Try this: http://stackoverflow.com/questions/21639415/create-archive-of-modified-files-in-git-via-batch-file – Will Huang Apr 01 '14 at 17:55
  • I get a "fatal: Not a valid object name" when executing `git archive -o ../../../stash.zip INDEX path/to/file/one path/to/file/two` – Stphane Oct 08 '15 at 16:10
5

If creating a patch is not an option, then how about something like this:

git stash
git checkout latest-commit
git diff --name-only previous-commit | zip ../changes.zip -@
git checkout master
git stash apply

NOTE: The git stash and git stash apply are only needed if you have working copy changes that have not been committed.

NOTE 2: git checkout latest-commit will put you on a detached head. Be sure to git checkout master when you are done or you could wind up having problems with future commits.

Tim Henigan
  • 60,452
  • 11
  • 85
  • 78
  • @Marnix van Valen Then why not turn it into a simple script (or batch file)? The problem is that you need to translate the list of file names into specific versions. The only other way I could think of is to use `git show latest-commit:file`. However, I was not able to come up with a one liner to do this. It would look something like this: `git diff --name-only previous-commit | xargs -I file git show latest-commit:file` ... but here is the problem. This prints the file (without its name) to STDOUT. How can this be translated into the zip file? – Tim Henigan Aug 06 '10 at 12:57
3

I have comme up with a solution, one line and quite simple:

zip archive.zip $(git diff-tree --no-commit-id --name-only -r latest-commit)

You can as well use any other archiving method like tar with this method.

2

In order to implement something useful to solve such a question for me, I've implemented a simple bash script. The script takes a revision hash and creates a tar.gz archive with all changed files including directory hierarchy. Anyone could use it or modify, I would be happy if it will help someone.

Just run script in the directory that is root for git repository and pass revision hash to the command line argumens.

Link to the script on hithub

  • The example looks llike this: #./do_delivery.sh -r a5ab48cdbb -n "vmeagt_delivery" ===> Archive filename: "vmeagt_delivery-20120430" ===> Revisions affected: "a5ab48cdbb-->HEAD" ===> Output path: "/Users/crible/Projects/ServerView_Agents/" ===> Git repository: "/Users/crible/Projects/ServerView_Agents/" ===> Creating tar.gz archive... ===> Done! –  Apr 30 '12 at 15:55
  • It is just the first version, that strongly need to be improved :) –  Apr 30 '12 at 15:56
0

try this, generate a patch_Ymd.zip from last commit:

git diff HEAD@{1} --name-only -z | xargs -0 git archive HEAD -o patch_$(date +'%Y%m%d').zip --

mindon
  • 318
  • 4
  • 13
-1

why not create a git patch which you can then apply to the other codebase?

$ git format-patch HEAD^..HEAD
knittl
  • 246,190
  • 53
  • 318
  • 364
  • 1
    Not everybody knows how to work with patch files I'm afraid. I use this to supply new releases to my customers. Unzipping an archive is something even non-developers can do. Applying a patch requires skills not all my customers have mastered (or even want to master). – Marnix van Valen Aug 06 '10 at 12:31