Last active
June 15, 2020 11:15
-
-
Save aleung/65dd468ec9a5d39f64862cab6a0b09ba to your computer and use it in GitHub Desktop.
Retrieve a file or a directory from git history at specific reversion
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# | |
# Usage: | |
# git-history-extract.py [-o <output_path>] <rev> [<path>] | |
# | |
# Retrieve a file or a directory from git history at specific reversion (tag or commit). | |
# The restored file(s) are saved into destination path. | |
# | |
# You must run it in a directory which is inside a git repository. | |
# | |
# Limitations: | |
# - Not support LFS | |
# - Not support symbolic link | |
import subprocess | |
import argparse | |
import os | |
def parse_arguments(): | |
parser = argparse.ArgumentParser( | |
description='Retrieve a file or a directory from git history at specific reversion') | |
parser.add_argument('-o', '--output-path', | |
help='Path to save restored file. Print file content at console if not specify.') | |
parser.add_argument('rev', | |
help='Revision of the commit to retrieve, can be SHA1 or tag. Default to current directory.') | |
parser.add_argument('path', nargs='?', default='./', | |
help='Path to the file or directory to retrieve') | |
return parser.parse_args() | |
def save_file(output_path, file_path, content, executable): | |
full_file_path = os.path.join(output_path, file_path) | |
dir_name = os.path.dirname(full_file_path) | |
print('Save {}'.format(full_file_path)) | |
os.makedirs(dir_name, exist_ok=True) | |
with open(full_file_path, 'wb') as file: | |
file.write(content) | |
if executable: | |
os.chmod(full_file_path, 0o755) | |
if __name__ == '__main__': | |
args = parse_arguments() | |
ls_tree = subprocess.check_output( | |
['git', 'ls-tree', '-rz', args.rev, args.path]).decode().split('\0') | |
for line in ls_tree: | |
if line: | |
meta, file = line.split('\t') | |
mode, _, object = meta.split(' ') | |
content = subprocess.check_output( | |
['git', 'cat-file', 'blob', object]) | |
if args.output_path: | |
save_file(args.output_path, file, content, | |
executable=(mode == '100755')) | |
else: | |
print('\n>>>>> {}'.format(file)) | |
try: | |
print(content.decode()) | |
except: | |
print('[ ... binary content ... ]') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Example: output to console | |
$ git-history-extract.py v3 | |
>>>>> 1658350664.jpg | |
[ ... binary content ... ] | |
>>>>> dir1.1/dir with space/name with space.x.y.txt | |
This file has LF ending. | |
LF | |
>>>>> dir1.1/script.sh | |
echo Runnable | |
>>>>> dir1.1/中文文件名.txt | |
This file has CRLF ending. | |
中文 | |
hello | |
# Example: output to folder | |
$ git-history-extract.py v3 -o ../ | |
Save ../1658350664.jpg | |
Save ../dir1.1/dir with space/name with space.x.y.txt | |
Save ../dir1.1/script.sh | |
Save ../dir1.1/中文文件名.txt | |
$ ls -l dir1.1 | |
total 16 | |
drwxr-xr-x 3 myid staff 96 Jun 12 14:05 dir with space | |
-rwxr-xr-x 1 myid staff 13 Jun 11 16:03 script.sh | |
-rw-r--r-- 1 myid staff 43 Jun 12 18:24 中文文件名.txt |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment