mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-08-11 17:17:42 +03:00
GitPlugin: Allow to ammend last commit. Improved thread safety. Reduced some calls.
This commit is contained in:
Binary file not shown.
@@ -190,6 +190,10 @@ selectbutton:disabled {
|
||||
border-color: var(--disabled-border);
|
||||
}
|
||||
|
||||
checkbox:disabled {
|
||||
color: var(--disabled-color);
|
||||
}
|
||||
|
||||
PushButton::icon,
|
||||
SelectButton::icon,
|
||||
TableView::cell::icon,
|
||||
|
||||
@@ -20,7 +20,7 @@ class EE_API Condition : NonCopyable {
|
||||
};
|
||||
|
||||
/** Initializes a Condition object and sets its internal value to value.
|
||||
* Thus using WaitAndLock(value, ...) will immediately return.
|
||||
* Thus using waitAndLock(value, ...) will immediately return.
|
||||
*/
|
||||
Condition( int value = 0 );
|
||||
|
||||
@@ -66,7 +66,7 @@ class EE_API Condition : NonCopyable {
|
||||
|
||||
void unlock();
|
||||
|
||||
/** Performs an assignement followed by a Signal() call.
|
||||
/** Performs an assignement followed by a signal() call.
|
||||
* The internal Condition value is updated to value() and the Condition is
|
||||
* signaled. Note that the Condition must be unlocked in order
|
||||
* to be updated, otherwise it'll block until the Condition
|
||||
@@ -92,13 +92,13 @@ class EE_API Condition : NonCopyable {
|
||||
void signal();
|
||||
|
||||
/** Signals the Condition and disables blocking calls,
|
||||
* thus WaitAndLock() does no more wait whatever
|
||||
* thus waitAndLock() does no more wait whatever
|
||||
* the awaitedValue is and waiting calls are unlocked, returning false.
|
||||
*/
|
||||
void invalidate();
|
||||
|
||||
/** Restores the blocking capabilities of the Condition,
|
||||
* possibly previously disabled with Invalidate()
|
||||
* possibly previously disabled with invalidate()
|
||||
*/
|
||||
void restore();
|
||||
|
||||
|
||||
2
src/thirdparty/subprocess/subprocess.h
vendored
2
src/thirdparty/subprocess/subprocess.h
vendored
@@ -1071,7 +1071,7 @@ int subprocess_join(struct subprocess_s *const process,
|
||||
|
||||
return 0;
|
||||
#else
|
||||
int status;
|
||||
int status = 0;
|
||||
|
||||
if (process->stdin_file) {
|
||||
fclose(process->stdin_file);
|
||||
|
||||
@@ -207,6 +207,10 @@ void IconManager::init( UISceneNode* sceneNode, FontTrueType* iconFont, FontTrue
|
||||
{ "repo-pull", 0xeb40 },
|
||||
{ "repo-push", 0xeb41 },
|
||||
{ "repo-forked", 0xea63 },
|
||||
{ "repo-fetch", 0xec1d },
|
||||
{ "git-fetch", 0xf101 },
|
||||
{ "git-commit", 0xeafc },
|
||||
{ "remove", 0xeb3b },
|
||||
{ "tag", 0xea66 } };
|
||||
|
||||
for ( const auto& icon : codIcons )
|
||||
|
||||
@@ -53,7 +53,7 @@ FormatterPlugin::~FormatterPlugin() {
|
||||
mWorkerCondition.wait( lock, [this]() { return mWorkersCount <= 0; } );
|
||||
}
|
||||
|
||||
for ( auto editor : mEditors ) {
|
||||
for ( auto& editor : mEditors ) {
|
||||
for ( auto& kb : mKeyBindings ) {
|
||||
editor.first->getKeyBindings().removeCommandKeybind( kb.first );
|
||||
if ( editor.first->hasDocument() )
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "git.hpp"
|
||||
#include <eepp/system/clock.hpp>
|
||||
#include <eepp/system/filesystem.hpp>
|
||||
#include <eepp/system/log.hpp>
|
||||
#include <eepp/system/luapattern.hpp>
|
||||
@@ -58,6 +59,7 @@ Git::Git( const std::string& projectDir, const std::string& gitPath ) : mGitPath
|
||||
}
|
||||
|
||||
int Git::git( const std::string& args, const std::string& projectDir, std::string& buf ) const {
|
||||
Clock clock;
|
||||
buf.clear();
|
||||
Process p;
|
||||
p.create( mGitPath, args,
|
||||
@@ -65,9 +67,10 @@ int Git::git( const std::string& args, const std::string& projectDir, std::strin
|
||||
Process::Options::InheritEnvironment,
|
||||
{ { "LC_ALL", "en_US.UTF-8" } }, projectDir.empty() ? mProjectPath : projectDir );
|
||||
p.readAllStdOut( buf );
|
||||
int retCode;
|
||||
int retCode = 0;
|
||||
p.join( &retCode );
|
||||
Log::info( "GitPlugin run: %s %s", mGitPath, args );
|
||||
Log::info( "GitPlugin cmd in %s (%d): %s %s", clock.getElapsedTime().toString(), retCode,
|
||||
mGitPath, args );
|
||||
return retCode;
|
||||
}
|
||||
|
||||
@@ -230,7 +233,8 @@ Git::Result Git::deleteBranch( const std::string& branch, const std::string& pro
|
||||
return gitSimple( String::format( "branch -D %s", branch ), projectDir );
|
||||
}
|
||||
|
||||
Git::Result Git::commit( const std::string& commitMsg, const std::string& projectDir ) {
|
||||
Git::Result Git::commit( const std::string& commitMsg, bool ammend,
|
||||
const std::string& projectDir ) {
|
||||
auto tmpPath = Sys::getTempPath() + ".ecode-git-commit-" + String::randString( 16 );
|
||||
if ( !FileSystem::fileWrite( tmpPath, commitMsg ) ) {
|
||||
Git::Result res;
|
||||
@@ -239,9 +243,9 @@ Git::Result Git::commit( const std::string& commitMsg, const std::string& projec
|
||||
return res;
|
||||
}
|
||||
std::string buf;
|
||||
int retCode =
|
||||
git( String::format( "commit --cleanup=whitespace --allow-empty --file=%s", tmpPath ),
|
||||
projectDir, buf );
|
||||
int retCode = git( String::format( "commit %s --cleanup=whitespace --allow-empty --file=%s",
|
||||
ammend ? "--ammend" : "", tmpPath ),
|
||||
projectDir, buf );
|
||||
FileSystem::fileRemove( tmpPath );
|
||||
Git::Result res;
|
||||
res.returnCode = retCode;
|
||||
|
||||
@@ -228,7 +228,7 @@ class Git {
|
||||
|
||||
Result deleteBranch( const std::string& branch, const std::string& projectDir = "" );
|
||||
|
||||
Result commit( const std::string& commitMsg, const std::string& projectDir = "" );
|
||||
Result commit( const std::string& commitMsg, bool ammend, const std::string& projectDir = "" );
|
||||
|
||||
Result fetch( const std::string& projectDir = "" );
|
||||
|
||||
|
||||
@@ -133,4 +133,14 @@ Git::Branch GitBranchModel::branch( const ModelIndex& index ) const {
|
||||
return *static_cast<Git::Branch*>( index.internalData() );
|
||||
}
|
||||
|
||||
Git::Branch GitBranchModel::branch( const std::string& name ) const {
|
||||
for ( const auto& type : mBranches ) {
|
||||
for ( const auto& branch : type.data ) {
|
||||
if ( branch.name == name )
|
||||
return branch;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace ecode
|
||||
|
||||
@@ -55,6 +55,8 @@ class GitBranchModel : public Model {
|
||||
|
||||
Git::Branch branch( const ModelIndex& index ) const;
|
||||
|
||||
Git::Branch branch( const std::string& name ) const;
|
||||
|
||||
protected:
|
||||
std::vector<BranchData> mBranches;
|
||||
GitPlugin* mPlugin{ nullptr };
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <eepp/system/luapattern.hpp>
|
||||
#include <eepp/system/scopedop.hpp>
|
||||
#include <eepp/ui/doc/syntaxdefinitionmanager.hpp>
|
||||
#include <eepp/ui/uicheckbox.hpp>
|
||||
#include <eepp/ui/uidropdownlist.hpp>
|
||||
#include <eepp/ui/uiiconthememanager.hpp>
|
||||
#include <eepp/ui/uiloader.hpp>
|
||||
@@ -46,6 +47,7 @@ std::string GitPlugin::statusTypeToString( Git::GitStatusType type ) {
|
||||
}
|
||||
|
||||
std::vector<std::string> GitPlugin::repos() {
|
||||
Lock l( mReposMutex );
|
||||
std::vector<std::string> ret;
|
||||
for ( const auto& repo : mRepos )
|
||||
ret.push_back( repo.first );
|
||||
@@ -296,13 +298,7 @@ void GitPlugin::updateStatus( bool force ) {
|
||||
if ( !mGit || mGit->getGitFolder().empty() )
|
||||
return;
|
||||
|
||||
decltype( mGitBranches ) prevBranch;
|
||||
{
|
||||
auto branches = mGit->branches( repos() );
|
||||
Lock l( mGitBranchMutex );
|
||||
prevBranch = mGitBranches;
|
||||
mGitBranches = std::move( branches );
|
||||
}
|
||||
auto prevBranch = updateReposBranches();
|
||||
Git::Status prevGitStatus;
|
||||
{
|
||||
Lock l( mGitStatusMutex );
|
||||
@@ -629,16 +625,48 @@ void GitPlugin::branchCreate() {
|
||||
msgBox->showWhenReady();
|
||||
}
|
||||
|
||||
void GitPlugin::commit() {
|
||||
void GitPlugin::commit( const std::string& repoPath ) {
|
||||
UIMessageBox* msgBox = UIMessageBox::New( UIMessageBox::TEXT_EDIT,
|
||||
i18n( "git_commit_message", "Commit Message:" ) );
|
||||
msgBox->on( Event::OnConfirm, [this, msgBox]( const Event* ) {
|
||||
|
||||
UICheckBox* chkBox = UICheckBox::New();
|
||||
chkBox->setLayoutMargin( Rectf( 0, 8, 0, 0 ) )
|
||||
->setLayoutSizePolicy( SizePolicy::WrapContent, SizePolicy::WrapContent )
|
||||
->setLayoutGravity( UI_HALIGN_LEFT | UI_VALIGN_CENTER )
|
||||
->setClipType( ClipType::None )
|
||||
->setParent( msgBox->getLayoutCont()->getFirstChild() )
|
||||
->setId( "git-ammend" );
|
||||
chkBox->setText( i18n( "git_ammend", "Ammend last commit" ) );
|
||||
chkBox->toPosition( 2 );
|
||||
|
||||
if ( !repoPath.empty() ) {
|
||||
auto branchName = mGitBranches[repoPath];
|
||||
if ( !branchName.empty() ) {
|
||||
if ( repoPath != repoSelected() || mBranchesTree->getModel() == nullptr ) {
|
||||
|
||||
auto branch = mGit->getAllBranchesAndTags( Git::RefType::All,
|
||||
"refs/heads/" + branchName, repoPath );
|
||||
if ( !branch.empty() )
|
||||
chkBox->setEnabled( branch.front().ahead > 0 );
|
||||
|
||||
} else {
|
||||
auto model = static_cast<const GitBranchModel*>( mBranchesTree->getModel() );
|
||||
auto branch = model->branch( branchName );
|
||||
if ( !branch.name.empty() )
|
||||
chkBox->setEnabled( branch.ahead > 0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
msgBox->on( Event::OnConfirm, [this, msgBox, chkBox]( const Event* ) {
|
||||
std::string msg( msgBox->getTextEdit()->getText().toUtf8() );
|
||||
if ( msg.empty() )
|
||||
return;
|
||||
msgBox->closeWindow();
|
||||
runAsync( [this, msg]() { return mGit->commit( msg ); }, true, true );
|
||||
runAsync( [this, msg, chkBox]() { return mGit->commit( msg, chkBox->isChecked() ); }, true,
|
||||
true );
|
||||
} );
|
||||
|
||||
msgBox->setCloseShortcut( { KEY_ESCAPE, KEYMOD_NONE } );
|
||||
msgBox->setTitle( i18n( "git_commit", "Commit" ) );
|
||||
msgBox->center();
|
||||
@@ -798,6 +826,32 @@ bool GitPlugin::onKeyDown( UICodeEditor* editor, const KeyEvent& event ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, std::string> GitPlugin::updateReposBranches() {
|
||||
mGit->getSubModules();
|
||||
|
||||
bool reposEmpty = false;
|
||||
{
|
||||
Lock l( mReposMutex );
|
||||
reposEmpty = mRepos.empty();
|
||||
}
|
||||
if ( reposEmpty )
|
||||
updateRepos();
|
||||
|
||||
Lock l( mGitBranchMutex );
|
||||
|
||||
decltype( mGitBranches ) prevBranch;
|
||||
if ( mGitBranches.empty() || mLastBranchesUpdate.getElapsedTime() > Seconds( 1 ) ) {
|
||||
prevBranch = mGitBranches;
|
||||
mGitBranches = mGit->branches( repos() );
|
||||
mLastBranchesUpdate.restart();
|
||||
} else {
|
||||
Lock l( mGitBranchMutex );
|
||||
prevBranch = mGitBranches;
|
||||
}
|
||||
|
||||
return prevBranch;
|
||||
}
|
||||
|
||||
void GitPlugin::updateBranches( bool force ) {
|
||||
if ( !mGit || !mGitFound || ( mRunningUpdateBranches && !force ) )
|
||||
return;
|
||||
@@ -808,15 +862,7 @@ void GitPlugin::updateBranches( bool force ) {
|
||||
if ( !mGit || mGit->getGitFolder().empty() )
|
||||
return;
|
||||
|
||||
mGit->getSubModules();
|
||||
|
||||
decltype( mGitBranches ) prevBranch;
|
||||
{
|
||||
Lock l( mGitBranchMutex );
|
||||
prevBranch = mGitBranches;
|
||||
mGitBranches = mGit->branches( repos() );
|
||||
}
|
||||
|
||||
auto prevBranch = updateReposBranches();
|
||||
auto branches = mGit->getAllBranchesAndTags( Git::RefType::All, {}, repoSelected() );
|
||||
auto hash = GitBranchModel::hashBranches( branches );
|
||||
auto model = GitBranchModel::asModel( std::move( branches ), hash, this );
|
||||
@@ -833,12 +879,7 @@ void GitPlugin::updateBranches( bool force ) {
|
||||
[this]( auto ) { mRunningUpdateBranches--; } );
|
||||
}
|
||||
|
||||
void GitPlugin::updateBranchesUI( std::shared_ptr<GitBranchModel> model ) {
|
||||
buildSidePanelTab();
|
||||
mBranchesTree->setModel( model );
|
||||
mBranchesTree->setColumnsVisible( { GitBranchModel::Name } );
|
||||
mBranchesTree->expandAll();
|
||||
|
||||
void GitPlugin::updateRepos() {
|
||||
if ( !mGit )
|
||||
return;
|
||||
|
||||
@@ -853,10 +894,19 @@ void GitPlugin::updateBranchesUI( std::shared_ptr<GitBranchModel> model ) {
|
||||
repos.insert( { std::move( subModulePath ), FileSystem::fileNameFromPath( subModule ) } );
|
||||
}
|
||||
|
||||
Lock l( mReposMutex );
|
||||
if ( repos == mRepos )
|
||||
return;
|
||||
|
||||
mRepos = std::move( repos );
|
||||
}
|
||||
|
||||
void GitPlugin::updateBranchesUI( std::shared_ptr<GitBranchModel> model ) {
|
||||
buildSidePanelTab();
|
||||
mBranchesTree->setModel( model );
|
||||
mBranchesTree->setColumnsVisible( { GitBranchModel::Name } );
|
||||
mBranchesTree->expandAll();
|
||||
updateRepos();
|
||||
std::vector<String> items;
|
||||
for ( const auto& repo : mRepos )
|
||||
items.push_back( repo.second );
|
||||
@@ -1020,11 +1070,15 @@ void GitPlugin::buildSidePanelTab() {
|
||||
if ( status->type == Git::GitStatusType::Staged ||
|
||||
status->type == Git::GitStatusType::Untracked ||
|
||||
status->type == Git::GitStatusType::Changed ) {
|
||||
std::string repoPath;
|
||||
if ( !status->files.empty() )
|
||||
repoPath = mGit->repoPath( status->files.front().file );
|
||||
|
||||
UIPopUpMenu* menu = UIPopUpMenu::New();
|
||||
menu->setId( "git_status_type_menu" );
|
||||
|
||||
if ( status->type == Git::GitStatusType::Staged ) {
|
||||
addMenuItem( menu, "git-commit", "Commit" );
|
||||
addMenuItem( menu, "git-commit", "Commit", "git-commit" );
|
||||
|
||||
addMenuItem( menu, "git-unstage-all", "Unstage All" );
|
||||
}
|
||||
@@ -1033,13 +1087,14 @@ void GitPlugin::buildSidePanelTab() {
|
||||
status->type == Git::GitStatusType::Changed )
|
||||
addMenuItem( menu, "git-stage-all", "Stage All" );
|
||||
|
||||
menu->on( Event::OnItemClicked, [this, model]( const Event* event ) {
|
||||
menu->on( Event::OnItemClicked, [this, model,
|
||||
repoPath]( const Event* event ) {
|
||||
if ( !mGit )
|
||||
return;
|
||||
UIMenuItem* item = event->getNode()->asType<UIMenuItem>();
|
||||
std::string id( item->getId() );
|
||||
if ( id == "git-commit" ) {
|
||||
commit();
|
||||
commit( repoPath );
|
||||
} else if ( id == "git-stage-all" ) {
|
||||
stage( model->getFiles( mGit->repoName( "" ),
|
||||
(Uint32)Git::GitStatusType::Untracked |
|
||||
@@ -1081,10 +1136,10 @@ void GitPlugin::openBranchMenu( const Git::Branch& branch ) {
|
||||
UIPopUpMenu* menu = UIPopUpMenu::New();
|
||||
menu->setId( "git_branch_menu" );
|
||||
|
||||
addMenuItem( menu, "git-fetch", "Fetch" );
|
||||
addMenuItem( menu, "git-fetch", "Fetch", "repo-fetch" );
|
||||
|
||||
if ( gitBranch() != branch.name ) {
|
||||
addMenuItem( menu, "git-checkout", "Check Out..." );
|
||||
addMenuItem( menu, "git-checkout", "Check Out...", "git-fetch" );
|
||||
}
|
||||
|
||||
if ( branch.type == Git::RefType::Head ) {
|
||||
@@ -1095,7 +1150,7 @@ void GitPlugin::openBranchMenu( const Git::Branch& branch ) {
|
||||
if ( branch.behind )
|
||||
addMenuItem( menu, "git-fast-forward-merge", "Fast Forward Merge" );
|
||||
menu->addSeparator();
|
||||
addMenuItem( menu, "git-branch-delete", "Delete" );
|
||||
addMenuItem( menu, "git-branch-delete", "Delete", "remove" );
|
||||
}
|
||||
|
||||
addMenuItem( menu, "git-create-branch", "Create Branch", "repo-forked", { KEY_F7 } );
|
||||
@@ -1176,6 +1231,7 @@ void GitPlugin::runAsync( std::function<Git::Result()> fn, bool _updateStatus, b
|
||||
}
|
||||
if ( _updateBranches )
|
||||
updateBranches();
|
||||
|
||||
if ( _updateStatus )
|
||||
updateStatus( true );
|
||||
} );
|
||||
|
||||
@@ -69,6 +69,10 @@ class GitPlugin : public PluginBase {
|
||||
|
||||
std::vector<std::string> repos();
|
||||
|
||||
std::unordered_map<std::string, std::string> updateReposBranches();
|
||||
|
||||
void updateRepos();
|
||||
|
||||
protected:
|
||||
std::unique_ptr<Git> mGit;
|
||||
std::unordered_map<std::string, std::string> mGitBranches;
|
||||
@@ -103,11 +107,13 @@ class GitPlugin : public PluginBase {
|
||||
UIWidget* mGitContentView{ nullptr };
|
||||
UIWidget* mGitNoContentView{ nullptr };
|
||||
UILoader* mLoader{ nullptr };
|
||||
std::atomic<int> mRunningUpdateBranches{ false };
|
||||
std::atomic<int> mRunningUpdateStatus{ false };
|
||||
std::atomic<int> mRunningUpdateBranches{ 0 };
|
||||
std::atomic<int> mRunningUpdateStatus{ 0 };
|
||||
Clock mLastBranchesUpdate;
|
||||
Mutex mGitBranchMutex;
|
||||
Mutex mGitStatusMutex;
|
||||
Mutex mRepoMutex;
|
||||
Mutex mReposMutex;
|
||||
|
||||
struct CustomTokenizer {
|
||||
SyntaxDefinition def;
|
||||
@@ -152,7 +158,7 @@ class GitPlugin : public PluginBase {
|
||||
|
||||
void branchCreate();
|
||||
|
||||
void commit();
|
||||
void commit( const std::string& repoPath = "" );
|
||||
|
||||
void stage( const std::vector<std::string>& files );
|
||||
|
||||
|
||||
@@ -274,10 +274,12 @@ void PluginManager::setFileSystemListener( FileSystemListener* listener ) {
|
||||
}
|
||||
|
||||
void PluginManager::subscribeFileSystemListener( Plugin* plugin ) {
|
||||
Lock l( mPluginsFSSubsMutex );
|
||||
mPluginsFSSubs.insert( plugin );
|
||||
}
|
||||
|
||||
void PluginManager::unsubscribeFileSystemListener( Plugin* plugin ) {
|
||||
Lock l( mPluginsFSSubsMutex );
|
||||
mPluginsFSSubs.erase( plugin );
|
||||
}
|
||||
|
||||
@@ -287,6 +289,7 @@ void PluginManager::subscribeFileSystemListener() {
|
||||
|
||||
mFileSystemListenerCb =
|
||||
mFileSystemListener->addListener( [this]( const FileEvent& ev, const FileInfo& file ) {
|
||||
Lock l( mPluginsFSSubsMutex );
|
||||
for ( Plugin* plugin : mPluginsFSSubs )
|
||||
plugin->onFileSystemEvent( ev, file );
|
||||
} );
|
||||
|
||||
@@ -357,6 +357,7 @@ class PluginManager {
|
||||
UICodeEditorSplitter* mSplitter{ nullptr };
|
||||
FileSystemListener* mFileSystemListener{ nullptr };
|
||||
Mutex mSubscribedPluginsMutex;
|
||||
Mutex mPluginsFSSubsMutex;
|
||||
SubscribedPlugins mSubscribedPlugins;
|
||||
OnLoadFileCb mLoadFileFn;
|
||||
Uint64 mFileSystemListenerCb{ 0 };
|
||||
|
||||
Reference in New Issue
Block a user