diff --git a/source/app.d b/source/app.d index 7592ee2..26110bb 100644 --- a/source/app.d +++ b/source/app.d @@ -12,6 +12,16 @@ int main(string[] args) .add(new Command("init", "Initializing the repository for storing snapshots")) .add(new Command("status", "Checking the status of tracked files")) .add(new Command("diff", "Show changed data")) + .add(new Command("export", "Export snapshot to a tar.gz archive") + .add(new Argument("path", "Output directory path for the archive").required) + .add(new Option("s", "snapshot", "Specify snapshot hash") + .optional + .validateEachWith( + opt => opt.isValidHash, + "must contain snapshot hash provided" + ) + ) + ) .add(new Command("create", "Create a new snapshot") .add(new Option("c", "comment", "Specify comment") .optional @@ -50,7 +60,7 @@ int main(string[] args) ) ) .add(new Command("restore", "Restore to the specified snapshot state") - .add(new Argument("hash", "hash").required) + .add(new Argument("snapshot", "Specify snapshot hash").required) ) .add(new Option("c", "config", "Сonfiguration file path") .optional @@ -100,7 +110,13 @@ int main(string[] args) ) ) .on("restore", restore => - snag.restore(restore.arg("hash")) + snag.restore(restore.arg("snapshot")) + ) + .on("export", e => + snag.exportSnapshot( + e.arg("path"), + e.option("snapshot", ""), + ) ); } catch (SnagException e) { e.print(); diff --git a/source/snag/core/core.d b/source/snag/core/core.d index 828563b..63f51d8 100644 --- a/source/snag/core/core.d +++ b/source/snag/core/core.d @@ -8,6 +8,8 @@ import std.array; import std.process; import std.algorithm; import std.string; +import std.file; +import std.path; import snag.lib; import snag.core.exception; @@ -22,10 +24,9 @@ class Snag { "git --git-dir=%s --work-tree=%s", config.git, config.project ).split(); + _config = config; - auto currentTime = Clock.currTime(); - _date = format("%02d%03d%02d%02d%02d", currentTime.year % 100, currentTime.dayOfYear, @@ -67,12 +68,10 @@ class Snag { ["status", "--porcelain"], "An error occurred while checking the file tracking status" ); - if (!result.output.length) { writeln("The current state of the files is up to date as of the latest snapshot"); return; } - writeln("The following list of files requires backup:"); result.output.split('\n').filter!(e => !e.strip.empty).each!((e) { writefln("\t/%s", e.strip.split[1]); @@ -205,18 +204,20 @@ class Snag { throw new SnagException( "Invalid snapshot hash provided" ); - auto result = git( ["status", "--porcelain"], "An error occurred while checking the file tracking status" ); - - if (result.output.length) + if (result.output.length) { git( ["restore", "."], "Failed to reset file changes state" ); - + git( + ["clean", "-fd"], + "Failed to clean untracked files" + ); + } git( ["rev-parse", hash], "This snapshot is not available in the archive" @@ -239,4 +240,48 @@ class Snag { } writeln("No changes at the moment"); } + + void exportSnapshot(string path, string hash) { + try { + !path.isDir && + throw new SnagException( + "Path is not a directory" + ); + } catch (Exception e) { + throw new SnagException( + "Failed to verify the archive output directory:\n\t" + ~ e.msg + ); + } + if (hash.length) { + !isValidHash(hash) && + throw new SnagException( + "Invalid snapshot hash provided" + ); + git( + ["rev-parse", hash], + "This snapshot is not available in the archive" + ); + } else { + hash = git( + ["rev-parse", "--short", "HEAD"], + "Failed to retrieve current snapshot information" + ).output.strip('\n'); + } + string file = buildPath( + path.absolutePath.buildNormalizedPath, + "%s-%s.tar.gz".format(_date, hash) + ); + git( + [ + "archive", + "--format=tar.gz", + hash, + "-o", + file + ], + "Failed to export snapshot to archive" + ); + writeln("Export to archive completed successfully: ", file); + } } diff --git a/source/snag/version_.d b/source/snag/version_.d index 0c40657..fad1eb1 100644 --- a/source/snag/version_.d +++ b/source/snag/version_.d @@ -1,3 +1,3 @@ module snag.version_; -enum snagVersion = "0.0.8"; +enum snagVersion = "0.0.9";