Search code examples
gitexportgit-archive

Export all commits into ZIP files or directories


How is it possible to export all commits into ZIP files (containing all files, not only patch/diff):

myproject-commit1-67d91ab.zip
myproject-commit2-9283acd.zip
myproject-commit3-c57daa6.zip
...

or into directories:

myproject-commit1-67d91ab/
myproject-commit2-9283acd/
myproject-commit3-c57daa6/

?

I was thinking about commands like:

git archive --format zip --output myproject-commit3.zip c57daa6

from How do I export a specific commit with git-archive?, but how to get all commits?

Notes:

  • the goal is to do an export of all files of each commit, so that I can have access to them even if I don't have git on the machine

  • the answer from How can I export Git change sets from one repository to another via sneaker net (external files)? creates a .bundle file, but it seems impossible to access it without git, so it's not what I'm looking for

  • this nearly works:

    for ((i = 0;; ++i)); do git checkout master~$i || break; tar czf ../myproject-commit$i.tgz .; done
    

    but it also archives all the files in the current directory which are not git added to the repo... How to avoid this problem?


Solution

  • You can't, except by the methods you are already thinking about.

    Specifically, to turn every commit into a zip archive (one separate archive per commit), simply iterate over every commit, turning each one into a zip archive.

    Your method for iterating simply needs to walk all possible commits, rather than walking only all first-parents of the master branch, and you must use git archive on each such commit. Hence:

    git rev-list --all |
        while read hash; do git archive ...options... $hash
    done
    

    The command git rev-list --all tells Git to print out every reachable commit hash ID, in some order. To change the order, use the various sorting options available to both git rev-list and git log (e.g., --author-date-order or --topo-order).

    If you don't want every commit—if you want instead only first-parents of master—you can still do this with git rev-list:

    git rev-list --first-parent master | ...
    

    Here, since Git is walking only first-parents starting from whichever commit master identifies, the hash IDs will be output in what Git considers forward order, i.e., backwards across the branch's first-parents:

    ...--o--o--o--o--o--o------o   <-- master
          \        \          /
           \        X--X--...X   <-- somebranch
            \         /
             X--X----X--X   <-- anotherbranch
    

    None of the X commits will appear since they are not on the first-parent lineage. (Without --first-parent, since all the commits on somebranch, and all but the last one on anotherbranch, are also on master, you would get all the X commits.)

    [Basj adds the following, which appears to be bash-specific due to $((i=i+1)):] Edit: this is a ready to use command to do it as described in the question:

    git rev-list --all --reverse | while read hash; do git archive --format zip --output ../myproject-commit$((i=i+1))-$hash.zip $hash; done
    

    [torek feels the need to add :-) : Note that the above enumerates commits sequentially, even if this is not an invertible mapping.]