import md5, os, osproc, parseopt2, strutils, parsecfg, streams, lapp # blimp is a little utility program for handling large files # in git repositories. Its inspired by git-fat and s3annex # but doesn't rely on S3 for storage, is a single binary without # need for Python, and has less features than git-fat. So far. # # Manual use: # # Use "blimp d mybigfile" to deflate it before commit. # Use "blimp i mybigfile" to inflate it back to original size. # # When deflated the file only has an md5sum string inside it. # # The file is copied over into: # /blimpStore/- # # Configuration is in: # /.blimp.conf # ~/blimpstore/.blimp.conf var blimpStore, remoteBlimpStore: string = nil var verbose: bool # Load blimp.conf file, overkill for now but... proc parseConfFile(filename: string) = var f = newFileStream(filename, fmRead) if f != nil: var p: CfgParser open(p, f, filename) while true: var e = next(p) case e.kind of cfgEof: break of cfgSectionStart: continue # Ignore of cfgKeyValuePair: if e.key == "remote": remoteBlimpStore = e.value else: quit("Unknown configuration: " & e.key) of cfgOption: quit("Unknown configuration: " & e.key) of cfgError: quit("Parsing " & filename & ": " & e.msg) close(p) # Trivial helper to enable verbose proc run(cmd: string): auto = if verbose: echo(cmd) execCmd(cmd) # Upload a file to the remote master blimpStore proc uploadFile(blimpFilename: string) = if remoteBlimpStore.isNil: echo("Remote blimpstore not set in configuration file, not uploading content:\n\t" & blimpFilename) return let errorCode = run("rsync -a " & blimpStore / blimpFilename & " " & remoteBlimpStore) if errorCode != 0: quit("Something went wrong uploading content to " & remoteBlimpStore, 2) # Download a file to the remote master blimpStore proc downloadFile(blimpFilename: string) = if remoteBlimpStore.isNil: quit("Remote blimpstore not set in configuration file, can not download content:\n\t" & blimpFilename) let errorCode = run("rsync -a " & remoteBlimpStore / blimpFilename & " " & blimpStore / "") if errorCode != 0: quit("Something went wrong downloading " & blimpFilename & " from " & remoteBlimpStore, 3) # Copy content to blimpStore, no upload yet. proc copyToBlimpStore(filename, blimpFilename: string) = if not existsFile(blimpStore / blimpFilename): copyFile(filename, blimpStore / blimpFilename) uploadFile(blimpFilename) # Copy content from blimpStore, and downloading first if needed proc copyFromblimpStore(blimpFilename, filename: string) = if not existsFile(blimpStore / blimpFilename): downloadFile(blimpFilename) copyFile(blimpStore / blimpFilename, filename) # Copy original file to blimpStore and replace with hash stub in git. proc deflate(filename: string) = var content: string try: content = readFile(filename) except: quit("Failed opening file: " & filename, 1) if content[0..4] == "hash:": quit("File is already deflated, ignored.", 5) let hash = getMD5(content) let blimpFilename = filename & "-" & hash copyToBlimpStore(filename, blimpFilename) writeFile(filename, "hash:" & blimpFilename) echo("\t" & filename & " deflated.") # Parse out hash from hash stub and copy back original content from blimpStore. proc inflate(filename: string) = var hashfile: File if not open(hashfile, filename): quit("Failed opening file: " & filename, 4) let hashline = split(string(readLine(hashfile)), {':'}) if hashline[0] == "hash": let blimpfilename = hashline[1] #removeFile(filename) copyFromblimpStore(blimpfilename, filename) else: quit("\t" & filename & " is not deflated.", 5) echo("\t" & filename & " inflated.") # Find git root dir or fall back on current dir proc gitRoot(): string = try: let tup = execCmdEx("git rev-parse --show-toplevel") if tup[1] == 0: result = tup[0] else: result = getCurrentDir() except: result = getCurrentDir() let help = """ blimp [options] -v,--verbose Verbosity (string) (i)nflate or (d)eflate (string...) One or more filepaths to inflate/deflate """ ################################ main ##################################### # Hardwired to "blimpstore" directory in home dir. blimpStore = getHomeDir() / "blimpstore" # Make sure we have the dir, or create it. try: if not existsDir(blimpStore): createDir(blimpStore) except: quit("Could not create " & blimpStore & " directory.", 1) # Parse configuration files if they exist parseConfFile(gitRoot() / ".blimp.conf") parseConfFile(blimpStore / ".blimp.conf") # Using lapp to get args let args = parse(help) let command = args["command"].asString let filenames = args["filenames"].asSeq verbose = args["verbose"].asBool # Do the deed if command == "d" or command == "deflate": for fn in filenames: deflate(fn.asString) elif command == "i" or command == "inflate": for fn in filenames: inflate(fn.asString) else: quit("Unknown command, only (d)eflate or (i)inflate are valid.", 6) # All good quit(0)