Commit 74272cdbacfafba0ff73372af3eb1568da46571e
0 parents
First commit
Showing
2 changed files
with
127 additions
and
0 deletions
README.md
0 → 100644
1 | +++ a/README.md |
fat.nim
0 → 100644
1 | +++ a/fat.nim | ||
1 | +import md5, os, osproc, parseopt2, strutils, parsecfg, streams | ||
2 | + | ||
3 | +# This is a little utility program for handling large files | ||
4 | +# in git repositories. Its inspired by git-fat and s3annex | ||
5 | +# but doesn't rely on S3 for storage. | ||
6 | +# | ||
7 | +# Use "fat d mybigfile" to deflate it before commit. | ||
8 | +# Use "fat i mybigfile" to inflate it back to original size. | ||
9 | +# | ||
10 | +# When deflated the file only has an md5sum string inside it. | ||
11 | +# | ||
12 | +# The file is copied over into: | ||
13 | +# <homedir>/fatstore/<originalfilename>-<md5sum> | ||
14 | + | ||
15 | +var fatstore, remoteFatstore: string | ||
16 | + | ||
17 | + | ||
18 | +# Load fatstore.conf file, overkill for now but... | ||
19 | +proc parseConfFile(filename: string) = | ||
20 | + echo filename | ||
21 | + var f = newFileStream(filename, fmRead) | ||
22 | + if f != nil: | ||
23 | + var p: CfgParser | ||
24 | + open(p, f, filename) | ||
25 | + while true: | ||
26 | + var e = next(p) | ||
27 | + case e.kind | ||
28 | + of cfgEof: | ||
29 | + break | ||
30 | + of cfgSectionStart: | ||
31 | + continue # Ignore | ||
32 | + of cfgKeyValuePair: | ||
33 | + if e.key == "remote": | ||
34 | + remoteFatstore = e.value | ||
35 | + else: | ||
36 | + quit("Unknown configuration: " & e.key) | ||
37 | + of cfgOption: | ||
38 | + quit("Unknown configuration: " & e.key) | ||
39 | + of cfgError: | ||
40 | + quit("Parsing " & filename & ": " & e.msg) | ||
41 | + close(p) | ||
42 | + | ||
43 | + | ||
44 | +# Upload a file to the remote master fatstore | ||
45 | +proc uploadFile(fatfilename: string) = | ||
46 | + if remoteFatstore.isNil: | ||
47 | + echo("Remote fatstore not set in configuration file, not uploading content:\n\t" & fatfilename) | ||
48 | + return | ||
49 | + let errorCode = execCmd("rsync -a " & fatstore / fatfilename & " " & remoteFatstore) | ||
50 | + if errorCode != 0: quit("Something went wrong uploading content to " & remoteFatstore, 2) | ||
51 | + | ||
52 | +# Download a file to the remote master fatstore | ||
53 | +proc downloadFile(fatfilename: string) = | ||
54 | + if remoteFatstore.isNil: | ||
55 | + quit("Remote fatstore not set in configuration file, can not download content:\n\t" & fatfilename) | ||
56 | + let errorCode = execCmd("rsync -a " & remoteFatstore / fatfilename & " " & fatstore / "") | ||
57 | + if errorCode != 0: quit("Something went wrong downloading " & fatfilename & " from " & remoteFatstore, 3) | ||
58 | + | ||
59 | + | ||
60 | +# Copy content to fatstore, no upload yet. | ||
61 | +proc copyToFatstore(filename, fatfilename: string) = | ||
62 | + if not existsFile(fatstore / fatfilename): | ||
63 | + copyFile(filename, fatstore / fatfilename) | ||
64 | + uploadFile(fatfilename) | ||
65 | + | ||
66 | +# Copy content from fatstore, and downloading first if needed | ||
67 | +proc copyFromFatstore(fatfilename, filename: string) = | ||
68 | + if not existsFile(fatstore / fatfilename): | ||
69 | + downloadFile(fatfilename) | ||
70 | + copyFile(fatstore / fatfilename, filename) | ||
71 | + | ||
72 | + | ||
73 | +# Copy original file to fatstore and replace with hash stub in git. | ||
74 | +proc deflate(filename: string) = | ||
75 | + let content = readFile(filename) | ||
76 | + if content[0..4] == "hash:": | ||
77 | + quit("File is already deflated, ignored.", 5) | ||
78 | + let hash = getMD5(content) | ||
79 | + let fatfilename = filename & "-" & hash | ||
80 | + copyToFatstore(filename, fatfilename) | ||
81 | + writeFile(filename, "hash:" & fatfilename) | ||
82 | + | ||
83 | +# Parse out hash from hash stub and copy back original content from fatstore. | ||
84 | +proc inflate(filename: string) = | ||
85 | + var hashfile: File | ||
86 | + if not open(hashfile, filename): | ||
87 | + quit("Could not open file: " & filename, 4) | ||
88 | + let hashline = split(string(readLine(hashfile)), {':'}) | ||
89 | + if hashline[0] == "hash": | ||
90 | + let fatfilename = hashline[1] | ||
91 | + #removeFile(filename) | ||
92 | + copyFromFatstore(fatfilename, filename) | ||
93 | + else: | ||
94 | + quit("File is not a fat file.", 5) | ||
95 | + | ||
96 | + | ||
97 | +################################ main ##################################### | ||
98 | + | ||
99 | +# Hardwired to "fatstore" directory in home dir. | ||
100 | +fatstore = getHomeDir() / "fatstore" | ||
101 | + | ||
102 | +# Make sure we have the dir, or create it. | ||
103 | +try: | ||
104 | + if not existsDir(fatstore): createDir(fatstore) | ||
105 | +except: | ||
106 | + quit("Could not create " & fatstore & " directory.", 1) | ||
107 | + | ||
108 | + | ||
109 | +# Parse configuration if it exists | ||
110 | +parseConfFile(fatstore / "fatstore.conf") | ||
111 | + | ||
112 | +# Only a command and a path as argument | ||
113 | +let argv = commandLineParams() | ||
114 | +let command = argv[0] | ||
115 | +let filename = argv[1] | ||
116 | + | ||
117 | + | ||
118 | +# Do the deed | ||
119 | +if command == "d" or command == "deflate": | ||
120 | + deflate(filename) | ||
121 | +elif command == "i" or command == "inflate": | ||
122 | + inflate(filename) | ||
123 | +else: | ||
124 | + quit("Unknown command, only (d)eflate or (i)inflate are valid.", 6) | ||
125 | + | ||
126 | +# All good | ||
127 | +quit(0) |