From 9177ee38f0a1f944de7e6a50e9c2146a15d3bcb7 Mon Sep 17 00:00:00 2001 From: Katelyn Gigante Date: Sun, 18 Oct 2020 14:04:34 +1100 Subject: [PATCH 1/6] Git functionality in par with existing svn --- src/util/io/package.cpp | 4 ++ src/util/vcs.cpp | 2 + src/util/vcs.hpp | 3 ++ src/util/vcs/git.cpp | 97 +++++++++++++++++++++++++++++++++++++++++ src/util/vcs/git.hpp | 25 +++++++++++ 5 files changed, 131 insertions(+) create mode 100644 src/util/vcs/git.cpp create mode 100644 src/util/vcs/git.hpp diff --git a/src/util/io/package.cpp b/src/util/io/package.cpp index 272cfa529..09ce0f157 100644 --- a/src/util/io/package.cpp +++ b/src/util/io/package.cpp @@ -425,6 +425,10 @@ void Package::saveToDirectory(const String& saveAs, bool remove_unused, bool is_ vcs->addFile(f_out_path); f.second.created = false; } + else + { + vcs->updateFile(f_out_path); + } } else if (filename != saveAs) { // save as, copy old filess if (isZipfile()) { diff --git a/src/util/vcs.cpp b/src/util/vcs.cpp index 00a715cc7..f3009e932 100644 --- a/src/util/vcs.cpp +++ b/src/util/vcs.cpp @@ -9,6 +9,7 @@ #include #include #include +#include // ----------------------------------------------------------------------------- : Reflection @@ -19,6 +20,7 @@ VCSP read_new(Reader& reader) { reader.handle(_("type"), type); if (type == _("none")) return make_intrusive(); else if (type == _("subversion")) return make_intrusive(); + else if (type == _("git")) return make_intrusive(); else if (type.empty()) { reader.warning(_ERROR_1_("expected key", _("version control system"))); throw ParseError(_ERROR_("aborting parsing")); diff --git a/src/util/vcs.hpp b/src/util/vcs.hpp index d334972d5..68c634df6 100644 --- a/src/util/vcs.hpp +++ b/src/util/vcs.hpp @@ -41,6 +41,9 @@ class VCS : public IntrusivePtrVirtualBase virtual void removeFile (const wxFileName& filename) { remove_file(filename.GetFullName()); } + /// Mark a file as updated + virtual void updateFile(const wxFileName& filename) { + } DECLARE_REFLECTION_VIRTUAL(); }; diff --git a/src/util/vcs/git.cpp b/src/util/vcs/git.cpp new file mode 100644 index 000000000..02cd33942 --- /dev/null +++ b/src/util/vcs/git.cpp @@ -0,0 +1,97 @@ +//+----------------------------------------------------------------------------+ +//| Description: Magic Set Editor - Program to make Magic (tm) cards | +//| Copyright: (C) Twan van Laarhoven and the other MSE developers | +//| License: GNU General Public License 2 or later (see file COPYING) | +//+----------------------------------------------------------------------------+ + +// ----------------------------------------------------------------------------- : Includes + +#include +#include +#include "wx/process.h" + +// ----------------------------------------------------------------------------- : SVN File Manipulation + +bool run_git(const Char** arguments, const wxString wd) { + wxProcess* process = new wxProcess(wxPROCESS_REDIRECT); + process->Redirect(); + wxExecuteEnv* env = new wxExecuteEnv(); + env->cwd = wd; + switch (wxExecute(const_cast(arguments), wxEXEC_SYNC, process, env)) { // Yuck, const_cast + // Success + case 0: + delete process; + delete env; + return true; + // Couldn't run Git + case -1: + handle_error(String(_("Can't run Git."))); + delete process; + delete env; + return false; + // Git error + default: + String error = String(_("Git encountered an error:\n")); + wxString log; + wxInputStream* err = process->GetErrorStream(); + + wxTextInputStream tStream(*err); + while (!err->Eof()) + { + log = tStream.ReadLine(); + error.append(log).append('\n'); + } + handle_error(error); + delete process; + delete env; + return false; + } + +} + +void GitVCS::addFile(const wxFileName& filename) +{ + String name = filename.GetFullPath(); + wxString wd = filename.GetPath(true); + const Char* name_c[] = { _("git"), _("add"), name.c_str(), nullptr }; + if (!run_git(name_c, wd)) { + VCS::addFile(filename); + } +} + +void GitVCS::moveFile(const wxFileName& source, const wxFileName& dest) +{ + String source_name = source.GetFullPath(), dest_name = dest.GetFullPath(); + const Char* name_c[] = { _("git"), _("mv"), source_name.c_str(), dest_name.c_str(), nullptr }; + wxString wd = source.GetPath(true); + if (!run_git(name_c, wd)) { + VCS::moveFile(source, dest); + } +} + +void GitVCS::removeFile(const wxFileName& filename) +{ + String name = filename.GetFullPath(); + const Char* name_c[] = { _("git"), _("rm"), name.c_str(), nullptr }; + wxString wd = filename.GetPath(true); + queue_message(MESSAGE_WARNING, String(name_c[0]) + name_c[1] + name_c[2]); + // `git rm` only removes it from the index. We still need to removed it from the filesystem. + VCS::removeFile(filename); + if (!run_git(name_c, wd)) { + VCS::removeFile(filename); + } +} + +void GitVCS::updateFile(const wxFileName& filename) { + // Git needs you to explicitly stage changes + this->addFile(filename); +} + +IMPLEMENT_REFLECTION(GitVCS) { + REFLECT_IF_NOT_READING{ + String type = _("git"); + REFLECT(type); + } +} + +// ----------------------------------------------------------------------------- : EOF diff --git a/src/util/vcs/git.hpp b/src/util/vcs/git.hpp new file mode 100644 index 000000000..e8341f239 --- /dev/null +++ b/src/util/vcs/git.hpp @@ -0,0 +1,25 @@ +//+----------------------------------------------------------------------------+ +//| Description: Magic Set Editor - Program to make Magic (tm) cards | +//| Copyright: (C) Twan van Laarhoven and the other MSE developers | +//| License: GNU General Public License 2 or later (see file COPYING) | +//+----------------------------------------------------------------------------+ + +#pragma once + +// ----------------------------------------------------------------------------- : Includes + +#include +#include + +// ----------------------------------------------------------------------------- : SubversionVCS + +class GitVCS : public VCS { +public: + void addFile(const wxFileName& filename) final; + void moveFile(const wxFileName& source, const wxFileName& destination) final; + void removeFile(const wxFileName& filename) final; + void updateFile(const wxFileName& filename) final; + + DECLARE_REFLECTION(); +}; + From 6a4ffe7847fc6a7323c0db6843187c3f489dc8b0 Mon Sep 17 00:00:00 2001 From: Katelyn Gigante Date: Mon, 19 Oct 2020 15:38:46 +1100 Subject: [PATCH 2/6] Commit git packages --- src/util/io/package.cpp | 1 + src/util/vcs.hpp | 6 ++++++ src/util/vcs/git.cpp | 6 +++++- src/util/vcs/git.hpp | 1 + src/util/vcs/subversion.cpp | 5 +++++ src/util/vcs/subversion.hpp | 1 + 6 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/util/io/package.cpp b/src/util/io/package.cpp index 09ce0f157..f6c86201b 100644 --- a/src/util/io/package.cpp +++ b/src/util/io/package.cpp @@ -445,6 +445,7 @@ void Package::saveToDirectory(const String& saveAs, bool remove_unused, bool is_ // old file, just keep it } } + vcs->commit(saveAs); } void Package::saveToZipfile(const String& saveAs, bool remove_unused, bool is_copy) { diff --git a/src/util/vcs.hpp b/src/util/vcs.hpp index 68c634df6..a88cea091 100644 --- a/src/util/vcs.hpp +++ b/src/util/vcs.hpp @@ -44,6 +44,12 @@ class VCS : public IntrusivePtrVirtualBase /// Mark a file as updated virtual void updateFile(const wxFileName& filename) { } + + /// Finalize changes and push them. + virtual void commit(const String& directory) { + } + + DECLARE_REFLECTION_VIRTUAL(); }; diff --git a/src/util/vcs/git.cpp b/src/util/vcs/git.cpp index 02cd33942..a8c38c60e 100644 --- a/src/util/vcs/git.cpp +++ b/src/util/vcs/git.cpp @@ -74,7 +74,6 @@ void GitVCS::removeFile(const wxFileName& filename) String name = filename.GetFullPath(); const Char* name_c[] = { _("git"), _("rm"), name.c_str(), nullptr }; wxString wd = filename.GetPath(true); - queue_message(MESSAGE_WARNING, String(name_c[0]) + name_c[1] + name_c[2]); // `git rm` only removes it from the index. We still need to removed it from the filesystem. VCS::removeFile(filename); if (!run_git(name_c, wd)) { @@ -87,6 +86,11 @@ void GitVCS::updateFile(const wxFileName& filename) { this->addFile(filename); } +void GitVCS::commit(const String& directory) { + const Char* name_c[] = { _("git"), _("commit"), nullptr }; + run_git(name_c, directory); +} + IMPLEMENT_REFLECTION(GitVCS) { REFLECT_IF_NOT_READING{ String type = _("git"); diff --git a/src/util/vcs/git.hpp b/src/util/vcs/git.hpp index e8341f239..cdea8a2dd 100644 --- a/src/util/vcs/git.hpp +++ b/src/util/vcs/git.hpp @@ -19,6 +19,7 @@ class GitVCS : public VCS { void moveFile(const wxFileName& source, const wxFileName& destination) final; void removeFile(const wxFileName& filename) final; void updateFile(const wxFileName& filename) final; + void commit(const String& directory) final; DECLARE_REFLECTION(); }; diff --git a/src/util/vcs/subversion.cpp b/src/util/vcs/subversion.cpp index ebece88ca..a444e7ee5 100644 --- a/src/util/vcs/subversion.cpp +++ b/src/util/vcs/subversion.cpp @@ -58,6 +58,11 @@ void SubversionVCS::removeFile(const wxFileName& filename) } } +void SubversionVCS::commit(const String& directory) { + // Implementing this might be a breaking change to existing svn users (if there are any) + // It also would require a bit of a refactoring (making run_svn do the extra things run_git does) +} + IMPLEMENT_REFLECTION(SubversionVCS) { REFLECT_IF_NOT_READING { String type = _("subversion"); diff --git a/src/util/vcs/subversion.hpp b/src/util/vcs/subversion.hpp index 2d18258a0..6740e751c 100644 --- a/src/util/vcs/subversion.hpp +++ b/src/util/vcs/subversion.hpp @@ -18,6 +18,7 @@ class SubversionVCS : public VCS { void addFile (const wxFileName& filename) final; void moveFile (const wxFileName& source, const wxFileName& destination) final; void removeFile (const wxFileName& filename) final; + void commit (const String& directory) final; DECLARE_REFLECTION(); }; From dda4adc76f2a2bfc18c97e350c3a290454488f25 Mon Sep 17 00:00:00 2001 From: Katelyn Gigante Date: Wed, 21 Oct 2020 16:22:04 +1100 Subject: [PATCH 3/6] Implement git pull/svn update --- .editorconfig | 3 +++ src/util/vcs.hpp | 3 +++ src/util/vcs/git.cpp | 9 ++++++++ src/util/vcs/git.hpp | 1 + src/util/vcs/subversion.cpp | 45 ++++++++++++++++++++++++++++++++----- src/util/vcs/subversion.hpp | 1 + 6 files changed, 56 insertions(+), 6 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..2955e47ba --- /dev/null +++ b/.editorconfig @@ -0,0 +1,3 @@ +[*.cpp,*.hpp] +indent_size = 2 +indent_style = space diff --git a/src/util/vcs.hpp b/src/util/vcs.hpp index a88cea091..348b32eba 100644 --- a/src/util/vcs.hpp +++ b/src/util/vcs.hpp @@ -49,6 +49,9 @@ class VCS : public IntrusivePtrVirtualBase virtual void commit(const String& directory) { } + virtual void pull(const String& directory) { + } + DECLARE_REFLECTION_VIRTUAL(); diff --git a/src/util/vcs/git.cpp b/src/util/vcs/git.cpp index a8c38c60e..9d4bda450 100644 --- a/src/util/vcs/git.cpp +++ b/src/util/vcs/git.cpp @@ -12,6 +12,8 @@ // ----------------------------------------------------------------------------- : SVN File Manipulation +// Maybe consider linking against libgit2, and making this a core feature? +// food for future thought. Could be useful for template devs? bool run_git(const Char** arguments, const wxString wd) { wxProcess* process = new wxProcess(wxPROCESS_REDIRECT); process->Redirect(); @@ -91,6 +93,13 @@ void GitVCS::commit(const String& directory) { run_git(name_c, directory); } +void GitVCS::pull(const String& directory) { + // TODO: Fetch, check status, and only pull if no conflicts. + const Char* name_c[] = { _("git"), _("pull"), nullptr }; + run_git(name_c, directory); +} + + IMPLEMENT_REFLECTION(GitVCS) { REFLECT_IF_NOT_READING{ String type = _("git"); diff --git a/src/util/vcs/git.hpp b/src/util/vcs/git.hpp index cdea8a2dd..00c41f487 100644 --- a/src/util/vcs/git.hpp +++ b/src/util/vcs/git.hpp @@ -20,6 +20,7 @@ class GitVCS : public VCS { void removeFile(const wxFileName& filename) final; void updateFile(const wxFileName& filename) final; void commit(const String& directory) final; + void pull(const String& directory) final; DECLARE_REFLECTION(); }; diff --git a/src/util/vcs/subversion.cpp b/src/util/vcs/subversion.cpp index a444e7ee5..28379e440 100644 --- a/src/util/vcs/subversion.cpp +++ b/src/util/vcs/subversion.cpp @@ -8,21 +8,44 @@ #include #include +#include // ----------------------------------------------------------------------------- : SVN File Manipulation -bool run_svn(const Char** arguments) { +bool run_svn(const Char** arguments, const wxString wd) { + wxProcess* process = new wxProcess(wxPROCESS_REDIRECT); + process->Redirect(); + wxExecuteEnv* env = new wxExecuteEnv(); + env->cwd = wd; switch (wxExecute(const_cast(arguments), wxEXEC_SYNC)) { // Yuck, const_cast // Success case 0: + delete process; + delete env; return true; // Couldn't run SVN case -1: handle_error(String(_("Can't run SVN."))); + delete process; + delete env; return false; // SVN error default: - handle_error(String(_("SVN encountered an error"))); + String error = String(_("SVN encountered an error\n")); + wxString log; + wxInputStream* err = process->GetErrorStream(); + if (err != NULL) { + wxTextInputStream tStream(*err); + while (!err->Eof()) + { + log = tStream.ReadLine(); + error.append(log).append('\n'); + } + } + + handle_error(error); + delete process; + delete env; return false; } @@ -32,7 +55,8 @@ void SubversionVCS::addFile(const wxFileName& filename) { String name = filename.GetFullPath(); const Char* name_c[] = {_("svn"), _("add"), name.c_str(), nullptr}; - if (!run_svn(name_c)) { + wxString wd = filename.GetPath(true); + if (!run_svn(name_c, wd)) { VCS::addFile(filename); } } @@ -41,7 +65,8 @@ void SubversionVCS::moveFile(const wxFileName& source, const wxFileName& dest) { String source_name = source.GetFullPath(), dest_name = dest.GetFullPath(); const Char* name_c[] = {_("svn"), _("mv"), source_name.c_str(), dest_name.c_str(), nullptr}; - if (!run_svn(name_c)) { + wxString wd = source.GetPath(true); + if (!run_svn(name_c, wd)) { VCS::moveFile(source, dest); } } @@ -50,17 +75,25 @@ void SubversionVCS::removeFile(const wxFileName& filename) { String name = filename.GetFullPath(); const Char* name_c[] = {_("svn"), _("rm"), name.c_str(), nullptr}; + wxString wd = filename.GetPath(true); queue_message(MESSAGE_WARNING, String(name_c[0]) + name_c[1] + name_c[2]); // TODO: do we really need to remove the file before calling "svn remove"? VCS::removeFile(filename); - if (!run_svn(name_c)) { + if (!run_svn(name_c, wd)) { VCS::removeFile(filename); } } void SubversionVCS::commit(const String& directory) { // Implementing this might be a breaking change to existing svn users (if there are any) - // It also would require a bit of a refactoring (making run_svn do the extra things run_git does) + + //const Char* name_c[] = { _("svn"), _("commit"), nullptr }; + //run_svn(name_c, directory); +} + +void SubversionVCS::pull(const String& directory) { + const Char* name_c[] = { _("svn"), _("up"), nullptr }; + run_svn(name_c, directory); } IMPLEMENT_REFLECTION(SubversionVCS) { diff --git a/src/util/vcs/subversion.hpp b/src/util/vcs/subversion.hpp index 6740e751c..a36c4b530 100644 --- a/src/util/vcs/subversion.hpp +++ b/src/util/vcs/subversion.hpp @@ -19,6 +19,7 @@ class SubversionVCS : public VCS { void moveFile (const wxFileName& source, const wxFileName& destination) final; void removeFile (const wxFileName& filename) final; void commit (const String& directory) final; + void pull (const String& directory) final; DECLARE_REFLECTION(); }; From 34f5c74982fe862ba816577b724c5827a85d75f2 Mon Sep 17 00:00:00 2001 From: Katelyn Gigante Date: Wed, 21 Oct 2020 16:23:16 +1100 Subject: [PATCH 4/6] Automatically detect vcs and pull before loading a package. --- src/data/set.cpp | 7 ++----- src/data/set.hpp | 6 ------ src/util/io/package.cpp | 16 +++++++++++++++- src/util/io/package.hpp | 10 ++++++++-- 4 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/data/set.cpp b/src/data/set.cpp index b1ae3da85..687164506 100644 --- a/src/data/set.cpp +++ b/src/data/set.cpp @@ -26,13 +26,11 @@ // ----------------------------------------------------------------------------- : Set Set::Set() - : vcs (make_intrusive()) - , script_manager(new SetScriptManager(*this)) + : script_manager(new SetScriptManager(*this)) {} Set::Set(const GameP& game) : game(game) - , vcs (make_intrusive()) , script_manager(new SetScriptManager(*this)) { data.init(game->set_fields); @@ -41,7 +39,6 @@ Set::Set(const GameP& game) Set::Set(const StyleSheetP& stylesheet) : game(stylesheet->game) , stylesheet(stylesheet) - , vcs (make_intrusive()) , script_manager(new SetScriptManager(*this)) { data.init(game->set_fields); @@ -204,7 +201,7 @@ IMPLEMENT_REFLECTION(Set) { REFLECT(pack_types); } reflect_set_info_get_member(handler,data); - REFLECT_NO_SCRIPT_N("version_control", vcs); + //REFLECT_NO_SCRIPT_N("version_control", vcs); REFLECT(apprentice_code); } diff --git a/src/data/set.hpp b/src/data/set.hpp index a46895af1..4042a522c 100644 --- a/src/data/set.hpp +++ b/src/data/set.hpp @@ -59,7 +59,6 @@ class Set : public Packaged { ActionStack actions; ///< Actions performed on this set and the cards in it KeywordDatabase keyword_db; ///< Database for matching keywords, must be cleared when keywords change - VCSP vcs; ///< The version control system to use /// A context for performing scripts /** Should only be used from the main thread! */ @@ -118,11 +117,6 @@ class Set : public Packaged { Version fileVersion() const override; /// Validate that the set is correctly loaded void validate(Version = app_version) override; - -protected: - VCSP getVCS() override { - return vcs; - } private: DECLARE_REFLECTION_OVERRIDE(); diff --git a/src/util/io/package.cpp b/src/util/io/package.cpp index f6c86201b..bf07d8b77 100644 --- a/src/util/io/package.cpp +++ b/src/util/io/package.cpp @@ -15,6 +15,9 @@ #include #include #include +#include +#include +#include // ----------------------------------------------------------------------------- : Package : outside @@ -23,6 +26,7 @@ IMPLEMENT_DYNAMIC_ARG(Package*, clipboard_package, nullptr); Package::Package() : zipStream (nullptr) + , vcs (nullptr) {} Package::~Package() { @@ -372,8 +376,18 @@ void Package::openDirectory(bool fast) { void Package::openSubdir(const String& name) { wxDir d(filename + _("/") + name); if (!d.IsOpened()) return; // ignore errors here - // find files String f; // filename + if (d.GetFirst(&f, _(".git"))) { + queue_message(MESSAGE_INFO, filename + _(" under git")); + vcs = make_intrusive(); + vcs->pull(filename); + } + if (d.GetFirst(&f, _(".svn"))) { + queue_message(MESSAGE_INFO, filename + _(" under subversion")); + vcs = make_intrusive(); + vcs->pull(filename); + } + // find files for(bool ok = d.GetFirst(&f, wxEmptyString, wxDIR_FILES | wxDIR_HIDDEN) ; ok ; ok = d.GetNext(&f)) { if (ignore_file(f)) continue; // add file to list of known files diff --git a/src/util/io/package.hpp b/src/util/io/package.hpp index 6d15df9c4..9e52c3dd3 100644 --- a/src/util/io/package.hpp +++ b/src/util/io/package.hpp @@ -88,6 +88,8 @@ class Package : public IntrusivePtrVirtualBase { Package(); virtual ~Package(); + VCSP vcs; ///< The version control system to use + /// Is a file opened? bool isOpened() const; /// Must the package be saved with saveAs()? @@ -182,8 +184,11 @@ class Package : public IntrusivePtrVirtualBase { } protected: - // TODO: I dislike putting this here very much. There ought to be a better way. - virtual VCSP getVCS() { return make_intrusive(); } + virtual VCSP getVCS() { + if (vcs == nullptr) + return make_intrusive(); + return vcs; + } /// true if this is a zip file, false if a directory bool isZipfile() const { return !wxDirExists(filename); } @@ -301,6 +306,7 @@ class Packaged : public Package { DECLARE_REFLECTION_VIRTUAL(); friend void after_reading(Packaged& p, Version file_app_version); + private: bool fully_loaded; ///< Is the package fully loaded? friend struct JustAsPackageProxy; From 6e9ed061cb5e36af65a7f31cb9fb117ddf423d81 Mon Sep 17 00:00:00 2001 From: Katelyn Gigante Date: Wed, 21 Oct 2020 19:14:24 +1100 Subject: [PATCH 5/6] Oops, missed a spot in the subversion implementation --- src/util/vcs/git.cpp | 1 - src/util/vcs/subversion.cpp | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/util/vcs/git.cpp b/src/util/vcs/git.cpp index 9d4bda450..7337da3a9 100644 --- a/src/util/vcs/git.cpp +++ b/src/util/vcs/git.cpp @@ -48,7 +48,6 @@ bool run_git(const Char** arguments, const wxString wd) { delete env; return false; } - } void GitVCS::addFile(const wxFileName& filename) diff --git a/src/util/vcs/subversion.cpp b/src/util/vcs/subversion.cpp index 28379e440..67ea71fed 100644 --- a/src/util/vcs/subversion.cpp +++ b/src/util/vcs/subversion.cpp @@ -17,7 +17,7 @@ bool run_svn(const Char** arguments, const wxString wd) { process->Redirect(); wxExecuteEnv* env = new wxExecuteEnv(); env->cwd = wd; - switch (wxExecute(const_cast(arguments), wxEXEC_SYNC)) { // Yuck, const_cast + switch (wxExecute(const_cast(arguments), wxEXEC_SYNC, process, env)) { // Yuck, const_cast // Success case 0: delete process; From a6616b6141b0aec8849fd54b4dc9e4faf75c9489 Mon Sep 17 00:00:00 2001 From: Katelyn Gigante Date: Mon, 2 Nov 2020 23:37:25 +1100 Subject: [PATCH 6/6] Do the `git push` for svn symmetry (And also because it's useful) --- src/util/vcs/git.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/util/vcs/git.cpp b/src/util/vcs/git.cpp index 7337da3a9..d853b6d2a 100644 --- a/src/util/vcs/git.cpp +++ b/src/util/vcs/git.cpp @@ -90,6 +90,8 @@ void GitVCS::updateFile(const wxFileName& filename) { void GitVCS::commit(const String& directory) { const Char* name_c[] = { _("git"), _("commit"), nullptr }; run_git(name_c, directory); + const Char* push_c[] = { _("git"), _("push"), nullptr }; + run_git(push_c, directory); } void GitVCS::pull(const String& directory) {