Compare commits

...

2 commits

Author SHA1 Message Date
fe55e8680f
0.0.5 2025-05-24 22:01:59 +03:00
a193f53871
Добавлены новые команды:
- list - получение списка бэкапов (снимков состояния)
- restore - восстановить состояние файлов указанного снимка
Изменено:
- create - создание снимков выполняется через ответвление и мердж ответвленной ветки

Текущие изменения позволяют создавать репозиторий, делать снимки состояния файлов и восстанавливать конкретное состояние
2025-05-24 21:59:01 +03:00
5 changed files with 201 additions and 16 deletions

3
.vscode/launch.json vendored
View file

@ -10,7 +10,8 @@
"dubBuild": true, "dubBuild": true,
"name": "Build & Debug DUB project", "name": "Build & Debug DUB project",
"cwd": "${command:dubWorkingDirectory}", "cwd": "${command:dubWorkingDirectory}",
"program": "bin/${command:dubTarget}" "program": "bin/${command:dubTarget}",
"args": ["list"]
} }
] ]
} }

View file

@ -1,6 +1,6 @@
{ {
"git": "/tmp/testgit", "git": "/tmp/testgit",
"project": "/home/alexander/Programming/new/dlang/snag/source", "project": "/tmp/test",
"email": "user@site.domain", "email": "user@site.domain",
"user": "snag", "user": "snag",
"presnag": [ "presnag": [

View file

@ -12,6 +12,10 @@ int main(string[] args)
.add(new Command("init", "Initializing the repository for storing snapshots")) .add(new Command("init", "Initializing the repository for storing snapshots"))
.add(new Command("status", "Checking the status of tracked files")) .add(new Command("status", "Checking the status of tracked files"))
.add(new Command("create", "Create a new backup")) .add(new Command("create", "Create a new backup"))
.add(new Command("list", "List of backups"))
.add(new Command("restore", "Restore to the specified snapshot state")
.add(new Argument("hash", "hash").required)
)
.add(new Option("c", "config", "Сonfiguration file path") .add(new Option("c", "config", "Сonfiguration file path")
.optional .optional
.validateEachWith( .validateEachWith(
@ -33,18 +37,24 @@ int main(string[] args)
} }
auto snag = new Snag(config); auto snag = new Snag(config);
import std.stdio;
try { try {
argumets argumets
.on("init", (init) { .on("init", init =>
snag.initialize(); snag.initialize()
}) )
.on("status", (status) { .on("status", status =>
snag.status(); snag.status()
}) )
.on("create", (create) { .on("create", create =>
snag.create(); snag.create()
}); )
.on("list", list =>
snag.list()
)
.on("restore", restore =>
snag.restore(restore.arg("hash"))
);
} catch (SnagException e) { } catch (SnagException e) {
e.print(); e.print();
return EXIT_FAILURE; return EXIT_FAILURE;

View file

@ -7,6 +7,7 @@ import std.array;
import std.process; import std.process;
import std.algorithm; import std.algorithm;
import std.string; import std.string;
import std.regex;
import snag.core.exception; import snag.core.exception;
@ -14,6 +15,11 @@ class Snag {
private string[] _baseCommand; private string[] _baseCommand;
private SnagConfig _config; 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) { this(SnagConfig config) {
_baseCommand = format( _baseCommand = format(
"git --git-dir=%s --work-tree=%s", "git --git-dir=%s --work-tree=%s",
@ -67,9 +73,9 @@ class Snag {
writeln("The following list of files requires backup:"); writeln("The following list of files requires backup:");
/** /**
Цепочка выполняет разбивку по переводу на новую строку, * Цепочка выполняет разбивку по переводу на новую строку,
отсеивает пустые строки, перебирает в цикле имеющиеся строки, * отсеивает пустые строки, перебирает в цикле имеющиеся строки,
выводит только вторую часть строки (разделенную по пробелу) * выводит только вторую часть строки (разделенную по пробелу)
*/ */
result.output.split('\n').filter!(e => !e.strip.empty).each!((e) { result.output.split('\n').filter!(e => !e.strip.empty).each!((e) {
writefln("\t/%s", e.strip.split[1]); writefln("\t/%s", e.strip.split[1]);
@ -77,7 +83,45 @@ class Snag {
} }
void create() { void create() {
/**
* Проверка файлов на состояние изменения
*/
auto result = execute( 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", "."] _baseCommand ~ ["add", "."]
); );
if (result.status) if (result.status)
@ -95,6 +139,136 @@ class Snag {
~ result.output ~ 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"); 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");
}
} }

View file

@ -1,3 +1,3 @@
module snag.version_; module snag.version_;
enum snagVersion = "0.0.4"; enum snagVersion = "0.0.5";