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

OP wanted to retrieve all versions, but the answers would not deliver. Especially if the file has hundreds of revisions (all suggestions are too manual). The only half-working solution was proposed by @Tobias in the comments, but suggested bash loop would build files in random order as well as it generates hundreds of empty files when used against our repos. One of the reasons was that “rev-list –all –objects” would list different objects (trees included – but useless for our purpose).

I started with Tobias’s solution, added counters, clean up a bit and end up reinventing the wheel in form of the bash script listed below.

The script would:

  • extract all file versions to /tmp/all_versions_exported
  • take 1 argument – relative path to the file inside git repo
  • give result filenames numeric prefix (sortable)
  • mention inspected filename in result files (to tell apples apart from oranges:)
  • mention commit date in the result filename (see output example below)
  • not create empty result files

cat /usr/local/bin/git_export_all_file_versions

#!/bin/bash

# we'll write all git versions of the file to this folder:
EXPORT_TO=/tmp/all_versions_exported

# take relative path to the file to inspect
GIT_PATH_TO_FILE=$1

# ---------------- don't edit below this line --------------

USAGE="Please cd to the root of your git proj and specify path to file you with to inspect (example: $0 some/path/to/file)"

# check if got argument
if [ "${GIT_PATH_TO_FILE}" == "" ]; then
    echo "error: no arguments given. ${USAGE}" >&2
    exit 1
fi

# check if file exist
if [ ! -f ${GIT_PATH_TO_FILE} ]; then
    echo "error: File '${GIT_PATH_TO_FILE}' does not exist. ${USAGE}" >&2
    exit 1
fi

# extract just a filename from given relative path (will be used in result file names)
GIT_SHORT_FILENAME=$(basename $GIT_PATH_TO_FILE)

# create folder to store all revisions of the file
if [ ! -d ${EXPORT_TO} ]; then
    echo "creating folder: ${EXPORT_TO}"
    mkdir ${EXPORT_TO}
fi

## uncomment next line to clear export folder each time you run script
#rm ${EXPORT_TO}/*

# reset coutner
COUNT=0

# iterate all revisions
git rev-list --all --objects -- ${GIT_PATH_TO_FILE} | \
    cut -d ' ' -f1 | \
while read h; do \
     COUNT=$((COUNT + 1)); \
     COUNT_PRETTY=$(printf "%04d" $COUNT); \
     COMMIT_DATE=`git show $h | head -3 | grep 'Date:' | awk '{print $4"-"$3"-"$6}'`; \
     if [ "${COMMIT_DATE}" != "" ]; then \
         git cat-file -p ${h}:${GIT_PATH_TO_FILE} > ${EXPORT_TO}/${COUNT_PRETTY}.${COMMIT_DATE}.${h}.${GIT_SHORT_FILENAME};\
     fi;\
done    

# return success code
echo "result stored to ${EXPORT_TO}"
exit 0

Usage example:

cd /home/myname/my-git-repo

git_export_all_file_versions docs/howto/readme.txt
    result stored to /tmp/all_versions_exported

ls /tmp/all_versions_exported
    0001.17-Oct-2016.ee0a1880ab815fd8f67bc4299780fc0b34f27b30.readme.txt
    0002.3-Oct-2016.d305158b94bedabb758ff1bb5e1ad74ed7ccd2c3.readme.txt
    0003.29-Sep-2016.7414a3de62529bfdd3cb1dd20ebc1a977793102f.readme.txt
    0004.28-Sep-2016.604cc0a34ec689606f7d3b2b5bbced1eece7483d.readme.txt
    0005.28-Sep-2016.198043c219c81d776c6d8a20e4f36bd6d8a57825.readme.txt
    0006.9-Sep-2016.5aea5191d4b86aec416b031cb84c2b78603a8b0f.readme.txt
    <and so on and on . . .>

Note #1: if you see errors like this:

fatal: Not a valid object name
3e93eba38b31b8b81905ceaa95eb47bbaed46494:readme.txt

it means you’ve started the script not from the root folder of your git project.

Note #2: if you want to get all versions of the file that was deleted few commits ago you will have to switch to any of the old commits where that file was present (not yet deleted) by command:

git checkout OLD_HASH_WHERE_FILE_EXISTED
git_export_all_file_versions path/to/existing/file.ext

Otherwise it will error out “file does not exist”. You don’t have to switch to the very last commit where the deleted file was last seen, instead it can be any old commit where the file was there and then “git_export_all_file_versions” will extract all versions (even from “future” commits relative to the old commit you switched to).

Leave a Comment

Hata!: SQLSTATE[HY000] [1045] Access denied for user 'divattrend_liink'@'localhost' (using password: YES)