gitgit-clonegit-checkout

git - getting ALL previous version of a specific file/folder


I want to retrieve all previous version of a specific file in a git repository.

I see it is possible to get one specific version with the checkout command, but I want them all. And the git clone command with the depth option doesn't seem to allow me to clone subfolder ("not valid repository name").

Do you know if it is possible and how?

Thank you


Solution

  • The script provided by Dmitry does actually solve the problem, but it had a few issues that led me to adapt it to be more suitable for my needs. Specifically:

    1. The use of git show broke because of my default date-format settings.
    2. I wanted the results sorted in date order, not reverse-date order.
    3. I wanted to be able to run it against a file that had been deleted from the repo.
    4. I didn't want all revisions on all branches; I just wanted the revisions reachable from HEAD.
    5. I wanted it to error if it wasn't in a git repo.
    6. I didn't want to have to edit the script to adjust certain options.
    7. The way it worked was inefficient.
    8. I didn't need the numbering in the output filenames. (A suitably-formatted date serves the same purpose.)
    9. I wanted safer "paths with spaces" handling

    You can see the latest version of my modifications in my github repo or here's the version as of this writing:

    #!/bin/sh
        
    # based on script provided by Dmitry Shevkoplyas at http://stackoverflow.com/questions/12850030/git-getting-all-previous-version-of-a-specific-file-folder
    
    set -e
    
    if ! git rev-parse --show-toplevel >/dev/null 2>&1 ; then
        echo "Error: you must run this from within a git working directory" >&2
        exit 1
    fi
    
    if [ "$#" -lt 1 ] || [ "$#" -gt 2 ]; then
        echo "Usage: $0 <relative path to file> [<output directory>]" >&2
        exit 2
    fi
    
    FILE_PATH="$1"
    
    EXPORT_TO=/tmp/all_versions_exported
    if [ -n "$2" ]; then
        EXPORT_TO="$2"
    fi
    
    FILE_NAME="$(basename "$FILE_PATH")"
    
    if [ ! -d "$EXPORT_TO" ]; then
        echo "Creating directory '$EXPORT_TO'"
        mkdir -p "$EXPORT_TO"
    fi
    
    echo "Writing files to '$EXPORT_TO'"
    git log --diff-filter=d --date-order --reverse --format="%ad %H" --date=iso-strict "$FILE_PATH" | grep -v '^commit' | \
        while read LINE; do \
            COMMIT_DATE=`echo $LINE | cut -d ' ' -f 1`; \
            COMMIT_SHA=`echo $LINE | cut -d ' ' -f 2`; \
            printf '.' ; \
            git cat-file -p "$COMMIT_SHA:$FILE_PATH" > "$EXPORT_TO/$COMMIT_DATE.$COMMIT_SHA.$FILE_NAME" ; \
        done
    echo
    
    exit 0
    

    An example of the output:

    $ git_export_all_file_versions bin/git_export_all_file_versions /tmp/stackoverflow/demo
    Creating directory '/tmp/stackoverflow/demo'
    Writing files to '/tmp/stackoverflow/demo'
    ...
    
    $ ls -1 /tmp/stackoverflow/demo/
    2017-05-02T15:52:52-04:00.c72640ed968885c3cc86812a2e1aabfbc2bc3b2a.git_export_all_file_versions
    2017-05-02T16:58:56-04:00.bbbcff388d6f75572089964e3dc8d65a3bdf7817.git_export_all_file_versions
    2017-05-02T17:05:50-04:00.67cbdeab97cd62813cec58d8e16d7c386c7dae86.git_export_all_file_versions