From a193f5387199956ddce5949280e81097fcbdefe7 Mon Sep 17 00:00:00 2001 From: Alexander Zhirov Date: Sat, 24 May 2025 21:59:01 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D1=8B=20=D0=BD=D0=BE=D0=B2=D1=8B=D0=B5=20=D0=BA=D0=BE?= =?UTF-8?q?=D0=BC=D0=B0=D0=BD=D0=B4=D1=8B:=20-=20list=20-=20=D0=BF=D0=BE?= =?UTF-8?q?=D0=BB=D1=83=D1=87=D0=B5=D0=BD=D0=B8=D0=B5=20=D1=81=D0=BF=D0=B8?= =?UTF-8?q?=D1=81=D0=BA=D0=B0=20=D0=B1=D1=8D=D0=BA=D0=B0=D0=BF=D0=BE=D0=B2?= =?UTF-8?q?=20(=D1=81=D0=BD=D0=B8=D0=BC=D0=BA=D0=BE=D0=B2=20=D1=81=D0=BE?= =?UTF-8?q?=D1=81=D1=82=D0=BE=D1=8F=D0=BD=D0=B8=D1=8F)=20-=20restore=20-?= =?UTF-8?q?=20=D0=B2=D0=BE=D1=81=D1=81=D1=82=D0=B0=D0=BD=D0=BE=D0=B2=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20=D1=81=D0=BE=D1=81=D1=82=D0=BE=D1=8F=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20=D1=84=D0=B0=D0=B9=D0=BB=D0=BE=D0=B2=20=D1=83=D0=BA?= =?UTF-8?q?=D0=B0=D0=B7=D0=B0=D0=BD=D0=BD=D0=BE=D0=B3=D0=BE=20=D1=81=D0=BD?= =?UTF-8?q?=D0=B8=D0=BC=D0=BA=D0=B0=20=D0=98=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D0=BE:=20-=20create=20-=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D1=81=D0=BD=D0=B8=D0=BC=D0=BA=D0=BE=D0=B2?= =?UTF-8?q?=20=D0=B2=D1=8B=D0=BF=D0=BE=D0=BB=D0=BD=D1=8F=D0=B5=D1=82=D1=81?= =?UTF-8?q?=D1=8F=20=D1=87=D0=B5=D1=80=D0=B5=D0=B7=20=D0=BE=D1=82=D0=B2?= =?UTF-8?q?=D0=B5=D1=82=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B8=20?= =?UTF-8?q?=D0=BC=D0=B5=D1=80=D0=B4=D0=B6=20=D0=BE=D1=82=D0=B2=D0=B5=D1=82?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D0=BD=D0=BE=D0=B9=20=D0=B2=D0=B5=D1=82?= =?UTF-8?q?=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Текущие изменения позволяют создавать репозиторий, делать снимки состояния файлов и восстанавливать конкретное состояние --- source/snag/core/core.d | 180 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 177 insertions(+), 3 deletions(-) diff --git a/source/snag/core/core.d b/source/snag/core/core.d index 06aad49..b09fb89 100644 --- a/source/snag/core/core.d +++ b/source/snag/core/core.d @@ -7,6 +7,7 @@ import std.array; import std.process; import std.algorithm; import std.string; +import std.regex; import snag.core.exception; @@ -14,6 +15,11 @@ class Snag { private string[] _baseCommand; private SnagConfig _config; + private bool isValidHash(string hash) { + auto hashPattern = ctRegex!r"^[a-fA-F0-9]{7}$"; + return !matchFirst(hash, hashPattern).empty; + } + this(SnagConfig config) { _baseCommand = format( "git --git-dir=%s --work-tree=%s", @@ -67,9 +73,9 @@ class Snag { 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]); @@ -77,7 +83,45 @@ class Snag { } void create() { + /** + * Проверка файлов на состояние изменения + */ auto result = execute( + _baseCommand ~ ["status", "--porcelain"] + ); + if (result.status) + throw new SnagException( + "An error occurred while checking the file tracking status:\n" + ~ result.output + ); + if (!result.output.length) + throw new SnagException( + "Current file state doesn't need to be archived again" + ); + + result = execute( + _baseCommand ~ [ + "rev-parse", "--short", "HEAD" + ] + ); + if (result.status) + throw new SnagException( + "Failed to retrieve current snapshot information:\n" + ~ result.output + ); + + string currentSnapshot = result.output.strip('\n'); + + result = execute( + _baseCommand ~ [ + "checkout", + "-b", + "mod", + currentSnapshot + ] + ); + + result = execute( _baseCommand ~ ["add", "."] ); if (result.status) @@ -95,6 +139,136 @@ class Snag { ~ result.output ); + result = execute( + _baseCommand ~ ["checkout", "master"] + ); + if (result.status) + throw new SnagException( + "Failed to perform intermediate state switching:\n" + ~ result.output + ); + + result = execute( + _baseCommand ~ [ + "merge", + "-X", + "theirs", + "--squash", + "mod" + ] + ); + if (result.status) + throw new SnagException( + "Failed to retrieve changes from the new intermediate snapshot:\n" + ~ result.output + ); + + result = execute( + _baseCommand ~ ["commit", "-m", "test"] + ); + if (result.status) + throw new SnagException( + "Failed to create backup after acquiring new intermediate snapshot:\n" + ~ result.output + ); + + result = execute( + _baseCommand ~ ["branch", "-D", "mod"] + ); + if (result.status) + throw new SnagException( + "Error while deleting temporary intermediate snapshot:\n" + ~ result.output + ); + writeln("Backup was created successfully"); } + + void list() { + auto result = execute( + _baseCommand ~ [ + "rev-parse", "--short", "HEAD" + ] + ); + if (result.status) + throw new SnagException( + "Failed to retrieve current snapshot information:\n" + ~ result.output + ); + + string current = result.output.strip('\n'); + + result = execute( + _baseCommand ~ [ + "log", + "--all", + "--date=format:%Y.%m.%d %H:%M", + "--pretty=format:%ad\t%h" + ] + ); + if (result.status) + throw new SnagException( + "Failed to retrieve the list of snapshots:\n" + ~ result.output + ); + + result.output.split('\n').each!((e) { + auto eArray = e.split('\t'); + if (current == eArray[1]) + writefln(" >\t%s\t%s", eArray[0], eArray[1]); + else + writefln("\t%s\t%s", eArray[0], eArray[1]); + }); + } + + void restore(string hash) { + if (!isValidHash(hash)) + throw new SnagException( + "Invalid snapshot hash provided" + ); + + auto result = execute( + _baseCommand ~ ["status", "--porcelain"] + ); + if (result.status) + throw new SnagException( + "An error occurred while checking the file tracking status:\n" + ~ result.output + ); + + if (result.output.length) { + result = execute( + _baseCommand ~ ["restore", "."] + ); + if (result.status) + throw new SnagException( + "Failed to reset file changes state:\n" + ~ result.output + ); + } + + result = execute( + _baseCommand ~ [ + "rev-parse", hash + ] + ); + if (result.status) + throw new SnagException( + "This snapshot is not available in the archive:\n" + ~ result.output.split('\n')[0] + ); + + result = execute( + _baseCommand ~ [ + "checkout", hash + ] + ); + if (result.status) + throw new SnagException( + "Failed to restore the snapshot state %s:\n" + .format(hash, result.output) + ); + + writeln("Backup was restored successfully"); + } }