Добавлена новая команда diff для просмотра внесенных изменений на текущий момент.

Добавлена возможность установки комментария, имени автора, электронной почты при создании снимка.
Функции проверок через регулярные выражения перенесены в новый модуль lib.
This commit is contained in:
Alexander Zhirov 2025-05-25 18:34:52 +03:00
parent eff4fa2fe6
commit 4301c27ca9
Signed by: alexander
GPG key ID: C8D8BE544A27C511
8 changed files with 90 additions and 36 deletions

View file

@ -2,7 +2,7 @@
"git": "/tmp/testgit",
"project": "/tmp/test",
"email": "user@site.domain",
"user": "snag",
"author": "snag",
"presnag": [
"/usr/bin/ls",
"/usr/local/bin/script.sh"

View file

@ -11,14 +11,37 @@ int main(string[] args)
auto argumets = new Program(programName, snagVersion)
.add(new Command("init", "Initializing the repository for storing snapshots"))
.add(new Command("status", "Checking the status of tracked files"))
.add(new Command("create", "Create a new backup"))
.add(new Command("list", "List of backups")
.add(new Command("diff", "Show changed data"))
.add(new Command("create", "Create a new snapshot")
.add(new Option("c", "comment", "Specify comment")
.optional
.validateEachWith(
opt => opt.length > 0,
"cannot be empty"
)
)
.add(new Option("a", "author", "Specify author")
.optional
.validateEachWith(
opt => opt.length > 0,
"cannot be empty"
)
)
.add(new Option("e", "email", "Specify email")
.optional
.validateEachWith(
opt => opt.isValidEmail,
"must contain an email address"
)
)
)
.add(new Command("list", "List of snapshots")
.add(new Flag("c", "comment", "Show comment")
.name("comment")
.optional
)
.add(new Flag("u", "user", "Show user")
.name("user")
.add(new Flag("a", "author", "Show author")
.name("author")
.optional
)
.add(new Flag("e", "email", "Show email")
@ -33,7 +56,7 @@ int main(string[] args)
.optional
.validateEachWith(
opt => opt.exists && opt.isFile,
"A JSON file path must be provided"
"must specify the path to the JSON file"
)
)
.parse(args);
@ -56,16 +79,23 @@ int main(string[] args)
.on("init", init =>
snag.initialize()
)
.on("diff", diff =>
snag.diff()
)
.on("status", status =>
snag.status()
)
.on("create", create =>
snag.create()
snag.create(
create.option("comment", ""),
create.option("author", ""),
create.option("email", "")
)
)
.on("list", list =>
snag.list(
list.flag("comment"),
list.flag("user"),
list.flag("author"),
list.flag("email")
)
)

View file

@ -3,8 +3,8 @@ module snag.config.config;
import std.json;
import std.file;
import std.path;
import std.regex;
import std.string;
import snag.lib;
import snag.config.exception;
@ -12,12 +12,7 @@ class SnagConfig {
private string _git;
private string _project;
private string _email;
private string _user;
private bool isValidEmail(string email) {
auto emailPattern = ctRegex!r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$";
return !matchFirst(email, emailPattern).empty;
}
private string _author;
this(string configFile) {
string jsonText;
@ -87,21 +82,21 @@ class SnagConfig {
~ _email
);
if ("user" !in jsonData)
if ("author" !in jsonData)
throw new SnagConfigException(
"The configuration file is missing the \"user\" parameter"
"The configuration file is missing the \"author\" parameter"
);
_user = jsonData["user"].str;
_author = jsonData["author"].str;
if (!_user.length)
if (!_author.length)
throw new SnagConfigException(
"The \"user\" parameter must contain an user name"
"The \"author\" parameter must contain an author name"
);
}
@property string git() const { return _git; }
@property string project() const { return _project; }
@property string email() const { return _email; }
@property string user() const { return _user; }
@property string author() const { return _author; }
}

View file

@ -8,8 +8,8 @@ import std.array;
import std.process;
import std.algorithm;
import std.string;
import std.regex;
import snag.lib;
import snag.core.exception;
class Snag {
@ -17,11 +17,6 @@ class Snag {
private SnagConfig _config;
private string _date;
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",
@ -58,7 +53,7 @@ class Snag {
"A Git repository initialization error occurred"
);
git(
["config", "user.name", _config.user],
["config", "user.name", _config.author],
"A Git repository initialization error occurred"
);
writeln(
@ -84,7 +79,7 @@ class Snag {
});
}
void create() {
void create(string comment, string author, string email) {
auto result = git(
["status", "--porcelain"],
"An error occurred while checking the file tracking status"
@ -96,11 +91,16 @@ class Snag {
"Current file state doesn't need to be archived again"
);
author.length && (environment["GIT_AUTHOR_NAME"] = author);
email.length && (environment["GIT_AUTHOR_EMAIL"] = email);
string message = comment.length ? comment : "Standard snapshot creation";
result = execute(_baseCommand ~ ["rev-parse", "--short", "HEAD"]);
if (result.status == 128) {
// Если это самый первый коммит после инициализации репозитория
git(["add", "."], "Failed to prepare files for archiving");
git(["commit", "-m", "test"], "Failed to create a backup");
git(["commit", "-m"] ~ message, "Failed to create a backup");
writeln("Backup was created successfully");
return;
} else if (result.status != 0)
@ -139,7 +139,7 @@ class Snag {
"Failed to prepare files for archiving"
);
git(
["commit", "-m", "test"],
["commit", "-m"] ~ message,
"Failed to create a backup"
);
newSnapshot = git(
@ -153,7 +153,7 @@ class Snag {
"Failed to prepare files for archiving"
);
git(
["commit", "-m", "test"],
["commit", "-m"] ~ message,
"Failed to create a backup"
);
newSnapshot = git(
@ -172,7 +172,7 @@ class Snag {
writeln("Backup was created successfully: ", newSnapshot);
}
void list(bool comment, bool user, bool email) {
void list(bool comment, bool author, bool email) {
string currentSnapshot = git(
["rev-parse", "--short", "HEAD"],
"Failed to retrieve current snapshot information"
@ -181,7 +181,7 @@ class Snag {
string format = "format:%h\t%ad";
comment && (format ~= "\t%s");
user && (format ~= "\t%an");
author && (format ~= "\t%an");
email && (format ~= "\t%ae");
git(
@ -193,7 +193,7 @@ class Snag {
],
"Failed to retrieve the list of snapshots"
).output.split('\n').map!(line => line.split('\t')).array.each!(e =>
writefln("%s\t%s",
writefln("%s\t%s",
currentSnapshot == e[0] ? " >" : "",
e.join("\t")
)
@ -227,4 +227,16 @@ class Snag {
);
writeln("Backup was restored successfully: ", hash);
}
void diff() {
auto result = git(
["diff"],
"Failed to retrieve changes"
);
if (result.output.length) {
result.output.write;
return;
}
writeln("No changes at the moment");
}
}

13
source/snag/lib/lib.d Normal file
View file

@ -0,0 +1,13 @@
module snag.lib.lib;
import std.regex;
bool isValidHash(string hash) {
auto hashPattern = ctRegex!r"^[a-fA-F0-9]{7}$";
return !matchFirst(hash, hashPattern).empty;
}
bool isValidEmail(string email) {
auto emailPattern = ctRegex!r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$";
return !matchFirst(email, emailPattern).empty;
}

View file

@ -0,0 +1,3 @@
module snag.lib;
public import snag.lib.lib;

View file

@ -1,5 +1,6 @@
module snag;
public import snag.version_;
public import snag.lib;
public import snag.config;
public import snag.core;

View file

@ -1,3 +1,3 @@
module snag.version_;
enum snagVersion = "0.0.7";
enum snagVersion = "0.0.8";