Добавлена новая команда diff для просмотра внесенных изменений на текущий момент.
Добавлена возможность установки комментария, имени автора, электронной почты при создании снимка. Функции проверок через регулярные выражения перенесены в новый модуль lib.
This commit is contained in:
parent
eff4fa2fe6
commit
4301c27ca9
8 changed files with 90 additions and 36 deletions
|
@ -2,7 +2,7 @@
|
||||||
"git": "/tmp/testgit",
|
"git": "/tmp/testgit",
|
||||||
"project": "/tmp/test",
|
"project": "/tmp/test",
|
||||||
"email": "user@site.domain",
|
"email": "user@site.domain",
|
||||||
"user": "snag",
|
"author": "snag",
|
||||||
"presnag": [
|
"presnag": [
|
||||||
"/usr/bin/ls",
|
"/usr/bin/ls",
|
||||||
"/usr/local/bin/script.sh"
|
"/usr/local/bin/script.sh"
|
||||||
|
|
44
source/app.d
44
source/app.d
|
@ -11,14 +11,37 @@ int main(string[] args)
|
||||||
auto argumets = new Program(programName, snagVersion)
|
auto argumets = new Program(programName, snagVersion)
|
||||||
.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("diff", "Show changed data"))
|
||||||
.add(new Command("list", "List of backups")
|
.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")
|
.add(new Flag("c", "comment", "Show comment")
|
||||||
.name("comment")
|
.name("comment")
|
||||||
.optional
|
.optional
|
||||||
)
|
)
|
||||||
.add(new Flag("u", "user", "Show user")
|
.add(new Flag("a", "author", "Show author")
|
||||||
.name("user")
|
.name("author")
|
||||||
.optional
|
.optional
|
||||||
)
|
)
|
||||||
.add(new Flag("e", "email", "Show email")
|
.add(new Flag("e", "email", "Show email")
|
||||||
|
@ -33,7 +56,7 @@ int main(string[] args)
|
||||||
.optional
|
.optional
|
||||||
.validateEachWith(
|
.validateEachWith(
|
||||||
opt => opt.exists && opt.isFile,
|
opt => opt.exists && opt.isFile,
|
||||||
"A JSON file path must be provided"
|
"must specify the path to the JSON file"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.parse(args);
|
.parse(args);
|
||||||
|
@ -56,16 +79,23 @@ int main(string[] args)
|
||||||
.on("init", init =>
|
.on("init", init =>
|
||||||
snag.initialize()
|
snag.initialize()
|
||||||
)
|
)
|
||||||
|
.on("diff", diff =>
|
||||||
|
snag.diff()
|
||||||
|
)
|
||||||
.on("status", status =>
|
.on("status", status =>
|
||||||
snag.status()
|
snag.status()
|
||||||
)
|
)
|
||||||
.on("create", create =>
|
.on("create", create =>
|
||||||
snag.create()
|
snag.create(
|
||||||
|
create.option("comment", ""),
|
||||||
|
create.option("author", ""),
|
||||||
|
create.option("email", "")
|
||||||
|
)
|
||||||
)
|
)
|
||||||
.on("list", list =>
|
.on("list", list =>
|
||||||
snag.list(
|
snag.list(
|
||||||
list.flag("comment"),
|
list.flag("comment"),
|
||||||
list.flag("user"),
|
list.flag("author"),
|
||||||
list.flag("email")
|
list.flag("email")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,8 +3,8 @@ module snag.config.config;
|
||||||
import std.json;
|
import std.json;
|
||||||
import std.file;
|
import std.file;
|
||||||
import std.path;
|
import std.path;
|
||||||
import std.regex;
|
|
||||||
import std.string;
|
import std.string;
|
||||||
|
import snag.lib;
|
||||||
|
|
||||||
import snag.config.exception;
|
import snag.config.exception;
|
||||||
|
|
||||||
|
@ -12,12 +12,7 @@ class SnagConfig {
|
||||||
private string _git;
|
private string _git;
|
||||||
private string _project;
|
private string _project;
|
||||||
private string _email;
|
private string _email;
|
||||||
private string _user;
|
private string _author;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
this(string configFile) {
|
this(string configFile) {
|
||||||
string jsonText;
|
string jsonText;
|
||||||
|
@ -87,21 +82,21 @@ class SnagConfig {
|
||||||
~ _email
|
~ _email
|
||||||
);
|
);
|
||||||
|
|
||||||
if ("user" !in jsonData)
|
if ("author" !in jsonData)
|
||||||
throw new SnagConfigException(
|
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(
|
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 git() const { return _git; }
|
||||||
@property string project() const { return _project; }
|
@property string project() const { return _project; }
|
||||||
@property string email() const { return _email; }
|
@property string email() const { return _email; }
|
||||||
@property string user() const { return _user; }
|
@property string author() const { return _author; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,8 @@ 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.lib;
|
||||||
import snag.core.exception;
|
import snag.core.exception;
|
||||||
|
|
||||||
class Snag {
|
class Snag {
|
||||||
|
@ -17,11 +17,6 @@ class Snag {
|
||||||
private SnagConfig _config;
|
private SnagConfig _config;
|
||||||
private string _date;
|
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) {
|
this(SnagConfig config) {
|
||||||
_baseCommand = format(
|
_baseCommand = format(
|
||||||
"git --git-dir=%s --work-tree=%s",
|
"git --git-dir=%s --work-tree=%s",
|
||||||
|
@ -58,7 +53,7 @@ class Snag {
|
||||||
"A Git repository initialization error occurred"
|
"A Git repository initialization error occurred"
|
||||||
);
|
);
|
||||||
git(
|
git(
|
||||||
["config", "user.name", _config.user],
|
["config", "user.name", _config.author],
|
||||||
"A Git repository initialization error occurred"
|
"A Git repository initialization error occurred"
|
||||||
);
|
);
|
||||||
writeln(
|
writeln(
|
||||||
|
@ -84,7 +79,7 @@ class Snag {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void create() {
|
void create(string comment, string author, string email) {
|
||||||
auto result = git(
|
auto result = git(
|
||||||
["status", "--porcelain"],
|
["status", "--porcelain"],
|
||||||
"An error occurred while checking the file tracking status"
|
"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"
|
"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"]);
|
result = execute(_baseCommand ~ ["rev-parse", "--short", "HEAD"]);
|
||||||
if (result.status == 128) {
|
if (result.status == 128) {
|
||||||
// Если это самый первый коммит после инициализации репозитория
|
// Если это самый первый коммит после инициализации репозитория
|
||||||
git(["add", "."], "Failed to prepare files for archiving");
|
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");
|
writeln("Backup was created successfully");
|
||||||
return;
|
return;
|
||||||
} else if (result.status != 0)
|
} else if (result.status != 0)
|
||||||
|
@ -139,7 +139,7 @@ class Snag {
|
||||||
"Failed to prepare files for archiving"
|
"Failed to prepare files for archiving"
|
||||||
);
|
);
|
||||||
git(
|
git(
|
||||||
["commit", "-m", "test"],
|
["commit", "-m"] ~ message,
|
||||||
"Failed to create a backup"
|
"Failed to create a backup"
|
||||||
);
|
);
|
||||||
newSnapshot = git(
|
newSnapshot = git(
|
||||||
|
@ -153,7 +153,7 @@ class Snag {
|
||||||
"Failed to prepare files for archiving"
|
"Failed to prepare files for archiving"
|
||||||
);
|
);
|
||||||
git(
|
git(
|
||||||
["commit", "-m", "test"],
|
["commit", "-m"] ~ message,
|
||||||
"Failed to create a backup"
|
"Failed to create a backup"
|
||||||
);
|
);
|
||||||
newSnapshot = git(
|
newSnapshot = git(
|
||||||
|
@ -172,7 +172,7 @@ class Snag {
|
||||||
writeln("Backup was created successfully: ", newSnapshot);
|
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(
|
string currentSnapshot = git(
|
||||||
["rev-parse", "--short", "HEAD"],
|
["rev-parse", "--short", "HEAD"],
|
||||||
"Failed to retrieve current snapshot information"
|
"Failed to retrieve current snapshot information"
|
||||||
|
@ -181,7 +181,7 @@ class Snag {
|
||||||
string format = "format:%h\t%ad";
|
string format = "format:%h\t%ad";
|
||||||
|
|
||||||
comment && (format ~= "\t%s");
|
comment && (format ~= "\t%s");
|
||||||
user && (format ~= "\t%an");
|
author && (format ~= "\t%an");
|
||||||
email && (format ~= "\t%ae");
|
email && (format ~= "\t%ae");
|
||||||
|
|
||||||
git(
|
git(
|
||||||
|
@ -193,7 +193,7 @@ class Snag {
|
||||||
],
|
],
|
||||||
"Failed to retrieve the list of snapshots"
|
"Failed to retrieve the list of snapshots"
|
||||||
).output.split('\n').map!(line => line.split('\t')).array.each!(e =>
|
).output.split('\n').map!(line => line.split('\t')).array.each!(e =>
|
||||||
writefln("%s\t%s",
|
writefln("%s\t%s",
|
||||||
currentSnapshot == e[0] ? " >" : "",
|
currentSnapshot == e[0] ? " >" : "",
|
||||||
e.join("\t")
|
e.join("\t")
|
||||||
)
|
)
|
||||||
|
@ -227,4 +227,16 @@ class Snag {
|
||||||
);
|
);
|
||||||
writeln("Backup was restored successfully: ", hash);
|
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
13
source/snag/lib/lib.d
Normal 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;
|
||||||
|
}
|
3
source/snag/lib/package.d
Normal file
3
source/snag/lib/package.d
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
module snag.lib;
|
||||||
|
|
||||||
|
public import snag.lib.lib;
|
|
@ -1,5 +1,6 @@
|
||||||
module snag;
|
module snag;
|
||||||
|
|
||||||
public import snag.version_;
|
public import snag.version_;
|
||||||
|
public import snag.lib;
|
||||||
public import snag.config;
|
public import snag.config;
|
||||||
public import snag.core;
|
public import snag.core;
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
module snag.version_;
|
module snag.version_;
|
||||||
|
|
||||||
enum snagVersion = "0.0.7";
|
enum snagVersion = "0.0.8";
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue