mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-10-01 18:50:16 +03:00
Git plugin: Added stash management.
UITreeView now selects items with right mouse click too.
This commit is contained in:
@@ -468,7 +468,8 @@ UITableRow* UIAbstractTableView::createRow() {
|
||||
rowWidget->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed );
|
||||
rowWidget->reloadStyle( true, true, true );
|
||||
rowWidget->on( Event::MouseDown, [this]( const Event* event ) {
|
||||
if ( !( event->asMouseEvent()->getFlags() & EE_BUTTON_LMASK ) || !isRowSelection() )
|
||||
if ( !( event->asMouseEvent()->getFlags() & ( EE_BUTTON_LMASK | EE_BUTTON_RMASK ) ) ||
|
||||
!isRowSelection() )
|
||||
return;
|
||||
auto index = event->getNode()->asType<UITableRow>()->getCurIndex();
|
||||
if ( getUISceneNode()->getWindow()->getInput()->isControlPressed() ) {
|
||||
|
||||
@@ -422,6 +422,24 @@ bool UITreeView::isExpanded( const ModelIndex& index ) const {
|
||||
return getIndexMetadata( index ).open;
|
||||
}
|
||||
|
||||
void UITreeView::setExpanded( const std::vector<ModelIndex>& indexes, bool expanded ) {
|
||||
if ( !getModel() )
|
||||
return;
|
||||
Model& model = *getModel();
|
||||
for ( const auto& index : indexes ) {
|
||||
if ( !index.isValid() )
|
||||
continue;
|
||||
size_t count = model.rowCount( index );
|
||||
if ( count )
|
||||
getIndexMetadata( index ).open = expanded;
|
||||
}
|
||||
createOrUpdateColumns( false );
|
||||
}
|
||||
|
||||
void UITreeView::setExpanded( const ModelIndex& index, bool expanded ) {
|
||||
setExpanded( std::vector{ index }, expanded );
|
||||
}
|
||||
|
||||
void UITreeView::setAllExpanded( const ModelIndex& index, bool expanded ) {
|
||||
Model& model = *getModel();
|
||||
size_t count = model.rowCount( index );
|
||||
|
||||
@@ -3038,10 +3038,6 @@ void App::initProjectTreeView( std::string path, bool openClean ) {
|
||||
tab->getTabWidget()->setTabSelected( tab );
|
||||
}
|
||||
} else { // ModelEventType::OpenMenu
|
||||
bool focusOnSelection = mProjectTreeView->getFocusOnSelection();
|
||||
mProjectTreeView->setFocusOnSelection( false );
|
||||
mProjectTreeView->getSelection().set( modelEvent->getModelIndex() );
|
||||
mProjectTreeView->setFocusOnSelection( focusOnSelection );
|
||||
mSettings->createProjectTreeMenu( FileInfo( path ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,6 +210,10 @@ void IconManager::init( UISceneNode* sceneNode, FontTrueType* iconFont, FontTrue
|
||||
{ "repo-fetch", 0xec1d },
|
||||
{ "git-fetch", 0xf101 },
|
||||
{ "git-commit", 0xeafc },
|
||||
{ "git-stash", 0xec26 },
|
||||
{ "git-stash-apply", 0xec27 },
|
||||
{ "git-stash-pop", 0xec28 },
|
||||
{ "diff-single", 0xec22 },
|
||||
{ "remove", 0xeb3b },
|
||||
{ "tag", 0xea66 } };
|
||||
|
||||
|
||||
@@ -214,6 +214,11 @@ Git::Result Git::add( std::vector<std::string> files, const std::string& project
|
||||
return gitSimple( String::format( "add --force -- %s", asList( files ) ), projectDir );
|
||||
}
|
||||
|
||||
Git::Result Git::stash( std::vector<std::string> files, const std::string& projectDir ) {
|
||||
return gitSimple( String::format( "stash push --include-untracked -- %s", asList( files ) ),
|
||||
projectDir );
|
||||
}
|
||||
|
||||
Git::Result Git::restore( const std::string& file, const std::string& projectDir ) {
|
||||
return gitSimple( String::format( "restore \"%s\"", file ), projectDir );
|
||||
}
|
||||
@@ -420,23 +425,39 @@ std::vector<Git::Branch> Git::getAllBranchesAndTags( RefType ref, std::string_vi
|
||||
std::vector<Branch> branches;
|
||||
std::string buf;
|
||||
|
||||
if ( EXIT_SUCCESS != git( args, projectDir, buf ) )
|
||||
return branches;
|
||||
if ( EXIT_SUCCESS == git( args, projectDir, buf ) ) {
|
||||
branches.reserve( countLines( buf ) );
|
||||
|
||||
getSubModules( projectDir );
|
||||
readAllLines( buf, [&]( const std::string_view& line ) {
|
||||
auto branch = String::trim( String::trim( line, '\'' ), '\t' );
|
||||
if ( ( ref & Head ) && String::startsWith( branch, "refs/heads/" ) ) {
|
||||
branches.emplace_back( parseLocalBranch( branch ) );
|
||||
} else if ( ( ref & Remote ) && String::startsWith( branch, "refs/remotes/" ) ) {
|
||||
branches.emplace_back( parseRemoteBranch( branch ) );
|
||||
} else if ( ( ref & Tag ) && String::startsWith( branch, "refs/tags/" ) ) {
|
||||
branches.emplace_back( parseTag( branch ) );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
branches.reserve( countLines( buf ) );
|
||||
|
||||
readAllLines( buf, [&]( const std::string_view& line ) {
|
||||
auto branch = String::trim( String::trim( line, '\'' ), '\t' );
|
||||
if ( ( ref & Head ) && String::startsWith( branch, "refs/heads/" ) ) {
|
||||
branches.emplace_back( parseLocalBranch( branch ) );
|
||||
} else if ( ( ref & Remote ) && String::startsWith( branch, "refs/remotes/" ) ) {
|
||||
branches.emplace_back( parseRemoteBranch( branch ) );
|
||||
} else if ( ( ref & Tag ) && String::startsWith( branch, "refs/tags/" ) ) {
|
||||
branches.emplace_back( parseTag( branch ) );
|
||||
}
|
||||
} );
|
||||
if ( ( ref & RefType::Stash ) && EXIT_SUCCESS == git( "stash list", projectDir, buf ) ) {
|
||||
branches.reserve( branches.size() + countLines( buf ) );
|
||||
LuaPattern pattern( "(stash@{%d+}):%s(.*)" );
|
||||
readAllLines( buf, [&]( const std::string_view& line ) {
|
||||
LuaPattern::Range matches[3];
|
||||
if ( pattern.matches( line.data(), 0, matches, line.size() ) ) {
|
||||
std::string id(
|
||||
line.substr( matches[1].start, matches[1].end - matches[1].start ) );
|
||||
std::string name(
|
||||
line.substr( matches[2].start, matches[2].end - matches[2].start ) );
|
||||
Git::Branch newBranch;
|
||||
newBranch.type = RefType::Stash;
|
||||
newBranch.name = std::move( name );
|
||||
newBranch.remote = std::move( id );
|
||||
branches.emplace_back( std::move( newBranch ) );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
return branches;
|
||||
}
|
||||
@@ -857,4 +878,26 @@ Git::GitStatusReport Git::statusFromShortStatusStr( const std::string_view& stat
|
||||
return { gitStatus, gitStatusType, gitStatusChar };
|
||||
}
|
||||
|
||||
Git::Result Git::stashPush( std::vector<std::string> files, const std::string& name, bool keepIndex,
|
||||
const std::string& projectDir ) {
|
||||
std::string args;
|
||||
if ( keepIndex )
|
||||
args += " --keep-index";
|
||||
if ( !name.empty() )
|
||||
args += " --message " + name;
|
||||
return gitSimple(
|
||||
String::format( "stash push --include-untracked %s -- %s", args, asList( files ) ),
|
||||
projectDir );
|
||||
}
|
||||
|
||||
Git::Result Git::stashApply( const std::string& stashId, bool restoreIndex,
|
||||
const std::string& projectDir ) {
|
||||
return gitSimple( String::format( "stash apply%s %s", restoreIndex ? " --index" : "", stashId ),
|
||||
projectDir );
|
||||
}
|
||||
|
||||
Git::Result Git::stashDrop( const std::string& stashId, const std::string& projectDir ) {
|
||||
return gitSimple( String::format( "stash drop %s", stashId ), projectDir );
|
||||
}
|
||||
|
||||
} // namespace ecode
|
||||
|
||||
@@ -83,6 +83,13 @@ class Git {
|
||||
Ignored = 1 << 5,
|
||||
};
|
||||
|
||||
static constexpr auto STATUS_TYPE_ALL =
|
||||
static_cast<std::underlying_type_t<GitStatusType>>( GitStatusType::Staged ) |
|
||||
static_cast<std::underlying_type_t<GitStatusType>>( GitStatusType::Changed ) |
|
||||
static_cast<std::underlying_type_t<GitStatusType>>( GitStatusType::Untracked ) |
|
||||
static_cast<std::underlying_type_t<GitStatusType>>( GitStatusType::Unmerged ) |
|
||||
static_cast<std::underlying_type_t<GitStatusType>>( GitStatusType::Ignored );
|
||||
|
||||
static constexpr auto ANY = 0;
|
||||
|
||||
enum StatusXY : uint16_t {
|
||||
@@ -179,12 +186,14 @@ class Git {
|
||||
Head = 0x1,
|
||||
Remote = 0x2,
|
||||
Tag = 0x4,
|
||||
All = 0x7,
|
||||
Stash = 0x8,
|
||||
All = 0xF,
|
||||
};
|
||||
|
||||
static constexpr const char* HEAD = "head";
|
||||
static constexpr const char* REMOTE = "remote";
|
||||
static constexpr const char* TAG = "tag";
|
||||
static constexpr const char* STASH = "stash";
|
||||
static constexpr const char* ALL = "all";
|
||||
|
||||
static const char* refTypeToString( RefType type ) {
|
||||
@@ -195,6 +204,8 @@ class Git {
|
||||
return REMOTE;
|
||||
case Tag:
|
||||
return TAG;
|
||||
case Stash:
|
||||
return STASH;
|
||||
case All:
|
||||
return ALL;
|
||||
}
|
||||
@@ -234,6 +245,8 @@ class Git {
|
||||
|
||||
Result add( std::vector<std::string> files, const std::string& projectDir = "" );
|
||||
|
||||
Result stash( std::vector<std::string> files, const std::string& projectDir = "" );
|
||||
|
||||
Result restore( const std::string& file, const std::string& projectDir = "" );
|
||||
|
||||
Result reset( std::vector<std::string> files, const std::string& projectDir = "" );
|
||||
@@ -320,6 +333,14 @@ class Git {
|
||||
|
||||
LogLevel getLogLevel() const { return mLogLevel; }
|
||||
|
||||
Result stashPush( std::vector<std::string> files, const std::string& name, bool keepIndex,
|
||||
const std::string& projectDir = "" );
|
||||
|
||||
Result stashApply( const std::string& stashId, bool restoreIndex,
|
||||
const std::string& projectDir = "" );
|
||||
|
||||
Result stashDrop( const std::string& stashId, const std::string& projectDir = "" );
|
||||
|
||||
protected:
|
||||
std::string mGitPath;
|
||||
std::string mProjectPath;
|
||||
|
||||
@@ -12,7 +12,7 @@ size_t GitBranchModel::hashBranches( const std::vector<Git::Branch>& branches )
|
||||
return hash;
|
||||
}
|
||||
|
||||
std::string GitBranchModel::refTypeToString( Git::RefType type ) {
|
||||
std::string GitBranchModel::refTypeToString( Git::RefType type ) const {
|
||||
switch ( type ) {
|
||||
case Git::RefType::Head:
|
||||
return mPlugin->i18n( "git_local_branches", "Local Branches" ).toUtf8();
|
||||
@@ -20,6 +20,8 @@ std::string GitBranchModel::refTypeToString( Git::RefType type ) {
|
||||
return mPlugin->i18n( "git_remote_branches", "Remote Branches" ).toUtf8();
|
||||
case Git::RefType::Tag:
|
||||
return mPlugin->i18n( "git_tags", "Tags" ).toUtf8();
|
||||
case Git::RefType::Stash:
|
||||
return mPlugin->i18n( "git_stashes", "Stashes" ).toUtf8();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -68,7 +70,10 @@ UIIcon* GitBranchModel::iconFor( const ModelIndex& index ) const {
|
||||
if ( index.column() == (Int64)treeColumn() ) {
|
||||
if ( index.hasParent() ) {
|
||||
Git::Branch* branch = static_cast<Git::Branch*>( index.internalData() );
|
||||
return mPlugin->findIcon( branch->type == Git::RefType::Tag ? GIT_TAG : GIT_REPO );
|
||||
return mPlugin->findIcon(
|
||||
branch->type == Git::RefType::Tag
|
||||
? GIT_TAG
|
||||
: ( branch->type == Git::RefType::Stash ? GIT_STASH : GIT_REPO ) );
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
@@ -143,4 +148,13 @@ Git::Branch GitBranchModel::branch( const std::string& name ) const {
|
||||
return {};
|
||||
}
|
||||
|
||||
ModelIndex GitBranchModel::refTypeIndex( Git::RefType refType ) const {
|
||||
auto name = refTypeToString( refType );
|
||||
for ( size_t i = 0; i < mBranches.size(); i++ ) {
|
||||
if ( mBranches[i].branch == name )
|
||||
return index( i, 0, {} );
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace ecode
|
||||
|
||||
@@ -31,7 +31,7 @@ class GitBranchModel : public Model {
|
||||
std::vector<Git::Branch> data;
|
||||
};
|
||||
|
||||
std::string refTypeToString( Git::RefType type );
|
||||
std::string refTypeToString( Git::RefType type ) const;
|
||||
|
||||
GitBranchModel( std::vector<Git::Branch>&& branches, size_t hash, GitPlugin* gitPlugin );
|
||||
|
||||
@@ -57,6 +57,7 @@ class GitBranchModel : public Model {
|
||||
|
||||
Git::Branch branch( const std::string& name ) const;
|
||||
|
||||
ModelIndex refTypeIndex( Git::RefType refType ) const;
|
||||
protected:
|
||||
std::vector<BranchData> mBranches;
|
||||
GitPlugin* mPlugin{ nullptr };
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <eepp/ui/uiiconthememanager.hpp>
|
||||
#include <eepp/ui/uiloader.hpp>
|
||||
#include <eepp/ui/uipopupmenu.hpp>
|
||||
#include <eepp/ui/uiradiobutton.hpp>
|
||||
#include <eepp/ui/uistackwidget.hpp>
|
||||
#include <eepp/ui/uistyle.hpp>
|
||||
#include <eepp/ui/uitextedit.hpp>
|
||||
@@ -605,18 +606,18 @@ void GitPlugin::branchDelete( Git::Branch branch ) {
|
||||
msgBox->showWhenReady();
|
||||
}
|
||||
|
||||
void GitPlugin::pull() {
|
||||
runAsync( [this]() { return mGit->pull( repoSelected() ); }, true, true, true );
|
||||
void GitPlugin::pull( const std::string& repoPath ) {
|
||||
runAsync( [this, repoPath]() { return mGit->pull( repoPath ); }, true, true, true );
|
||||
}
|
||||
|
||||
void GitPlugin::push() {
|
||||
void GitPlugin::push( const std::string& repoPath ) {
|
||||
UIMessageBox* msgBox = UIMessageBox::New(
|
||||
UIMessageBox::OK_CANCEL,
|
||||
i18n( "git_confirm_push_changes",
|
||||
"Are you sure you want to push the local changes to the remote server?" ) );
|
||||
|
||||
msgBox->on( Event::OnConfirm, [this]( auto ) {
|
||||
runAsync( [this]() { return mGit->push( repoSelected() ); }, true, true, true, true );
|
||||
msgBox->on( Event::OnConfirm, [this, repoPath]( auto ) {
|
||||
runAsync( [this, repoPath]() { return mGit->push( repoPath ); }, true, true, true, true );
|
||||
} );
|
||||
msgBox->setCloseShortcut( { KEY_ESCAPE, KEYMOD_NONE } );
|
||||
msgBox->setTitle( i18n( "git_confirm", "Confirm" ) );
|
||||
@@ -624,8 +625,8 @@ void GitPlugin::push() {
|
||||
msgBox->showWhenReady();
|
||||
}
|
||||
|
||||
void GitPlugin::fetch() {
|
||||
runAsync( [this]() { return mGit->fetch( repoSelected() ); }, true, true, true );
|
||||
void GitPlugin::fetch( const std::string& repoPath ) {
|
||||
runAsync( [this, repoPath]() { return mGit->fetch( repoPath ); }, true, true, true );
|
||||
}
|
||||
|
||||
void GitPlugin::branchCreate() {
|
||||
@@ -683,6 +684,16 @@ void GitPlugin::commit( const std::string& repoPath ) {
|
||||
chkBypassHook->setText( i18n( "git_bypass_hook", "Bypass commit hook" ) );
|
||||
chkBypassHook->toPosition( 3 );
|
||||
|
||||
UICheckBox* chkPush = UICheckBox::New();
|
||||
chkPush->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-push-commit" );
|
||||
chkPush->setText( i18n( "git_push_commit", "Push commit" ) );
|
||||
chkPush->toPosition( 4 );
|
||||
|
||||
if ( !repoPath.empty() ) {
|
||||
auto branchName = mGitBranches[repoPath];
|
||||
if ( !branchName.empty() ) {
|
||||
@@ -700,24 +711,28 @@ void GitPlugin::commit( const std::string& repoPath ) {
|
||||
}
|
||||
}
|
||||
|
||||
msgBox->on( Event::OnConfirm, [this, msgBox, chkAmmend, chkBypassHook]( const Event* ) {
|
||||
std::string msg( msgBox->getTextEdit()->getText().toUtf8() );
|
||||
if ( msg.empty() )
|
||||
return;
|
||||
bool ammend = chkAmmend->isChecked();
|
||||
bool bypassHook = chkBypassHook->isChecked();
|
||||
msgBox->closeWindow();
|
||||
runAsync(
|
||||
[this, msg, ammend, bypassHook, msgBox]() {
|
||||
auto res = mGit->commit( msg, ammend, bypassHook );
|
||||
if ( res.success() )
|
||||
mLastCommitMsg.clear();
|
||||
else
|
||||
mLastCommitMsg = msgBox->getTextEdit()->getText();
|
||||
return res;
|
||||
},
|
||||
true, true );
|
||||
} );
|
||||
msgBox->on( Event::OnConfirm,
|
||||
[this, msgBox, chkAmmend, chkBypassHook, chkPush, repoPath]( const Event* ) {
|
||||
std::string msg( msgBox->getTextEdit()->getText().toUtf8() );
|
||||
if ( msg.empty() )
|
||||
return;
|
||||
bool ammend = chkAmmend->isChecked();
|
||||
bool bypassHook = chkBypassHook->isChecked();
|
||||
bool pushCommit = chkPush->isChecked();
|
||||
msgBox->closeWindow();
|
||||
runAsync(
|
||||
[this, msg, ammend, bypassHook, msgBox, pushCommit, repoPath]() {
|
||||
auto res = mGit->commit( msg, ammend, bypassHook, repoPath );
|
||||
if ( res.success() ) {
|
||||
mLastCommitMsg.clear();
|
||||
if ( pushCommit )
|
||||
return mGit->push( repoPath );
|
||||
} else
|
||||
mLastCommitMsg = msgBox->getTextEdit()->getText();
|
||||
return res;
|
||||
},
|
||||
true, true, true, true );
|
||||
} );
|
||||
|
||||
msgBox->on( Event::OnCancel, [this, msgBox]( const Event* ) {
|
||||
mLastCommitMsg = msgBox->getTextEdit()->getText();
|
||||
@@ -840,6 +855,104 @@ void GitPlugin::diff( const std::string& file, bool isStaged ) {
|
||||
|
||||
// File operations
|
||||
|
||||
// Stash operations
|
||||
|
||||
void GitPlugin::stashPush( const std::vector<std::string>& files, const std::string& repoPath ) {
|
||||
|
||||
UIMessageBox* msgBox = UIMessageBox::New(
|
||||
UIMessageBox::INPUT,
|
||||
i18n( "git_stash_push", "Stash Local Changes\nName your stash (optional):" ) );
|
||||
|
||||
UIRadioButton* rKeepIndex = UIRadioButton::New();
|
||||
rKeepIndex->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-stash-keep-index" );
|
||||
rKeepIndex->setText( i18n( "git_stash_keep_index", "Keep Index" ) );
|
||||
rKeepIndex->toPosition( 2 );
|
||||
|
||||
UIRadioButton* rKeepWorkingTree = UIRadioButton::New();
|
||||
rKeepWorkingTree->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-stash-keep-working-tree" );
|
||||
rKeepWorkingTree->setText( i18n( "git_stash_keep_working_tree", "Keep Working Tree" ) );
|
||||
rKeepWorkingTree->toPosition( 3 );
|
||||
|
||||
msgBox->on( Event::OnConfirm,
|
||||
[this, msgBox, rKeepIndex, rKeepWorkingTree, files, repoPath]( const Event* ) {
|
||||
bool keepIndex = rKeepIndex->isActive();
|
||||
bool keepWorkingTree = rKeepWorkingTree->isActive();
|
||||
std::string message = msgBox->getTextInput()->getText().toUtf8();
|
||||
String::trimInPlace( message );
|
||||
String::trimInPlace( message, '\n' );
|
||||
msgBox->closeWindow();
|
||||
runAsync(
|
||||
[this, files, keepIndex, keepWorkingTree, repoPath, message]() {
|
||||
auto res = mGit->stashPush( files, message, keepIndex, repoPath );
|
||||
if ( res.success() && keepWorkingTree )
|
||||
mGit->stashApply( "stash@{0}", true, repoPath );
|
||||
return res;
|
||||
},
|
||||
true, true );
|
||||
} );
|
||||
|
||||
msgBox->setCloseShortcut( { KEY_ESCAPE, KEYMOD_NONE } );
|
||||
msgBox->setTitle( i18n( "git_stash_save", "Save Stash" ) );
|
||||
msgBox->center();
|
||||
msgBox->showWhenReady();
|
||||
}
|
||||
|
||||
void GitPlugin::stashApply( const Git::Branch& branch ) {
|
||||
UIMessageBox* msgBox = UIMessageBox::New(
|
||||
UIMessageBox::OK_CANCEL,
|
||||
i18n( "git_confirm_apply_stash", "Apply a previously saved stash?" ).toUtf8() );
|
||||
|
||||
UICheckBox* chkIndex = UICheckBox::New();
|
||||
chkIndex->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-restore-index" );
|
||||
chkIndex->setText( i18n( "git_restore_index", "Restore Index" ) );
|
||||
chkIndex->toPosition( 2 );
|
||||
chkIndex->setChecked( true );
|
||||
|
||||
msgBox->on( Event::OnConfirm, [this, branch, chkIndex]( auto ) {
|
||||
runAsync(
|
||||
[this, branch, chkIndex]() {
|
||||
return mGit->stashApply( branch.remote, chkIndex->isChecked(), repoSelected() );
|
||||
},
|
||||
true, true );
|
||||
} );
|
||||
msgBox->setCloseShortcut( { KEY_ESCAPE, KEYMOD_NONE } );
|
||||
msgBox->setTitle( i18n( "git_apply_stash_title", "Apply Stash" ) );
|
||||
msgBox->center();
|
||||
msgBox->showWhenReady();
|
||||
}
|
||||
|
||||
void GitPlugin::stashDrop( const Git::Branch& branch ) {
|
||||
UIMessageBox* msgBox = UIMessageBox::New(
|
||||
UIMessageBox::OK_CANCEL,
|
||||
i18n( "git_confirm_drop_stash", "Do you want to drop the selected stash?" ).toUtf8() );
|
||||
|
||||
msgBox->on( Event::OnConfirm, [this, branch]( auto ) {
|
||||
runAsync( [this, branch]() { return mGit->stashDrop( branch.remote, repoSelected() ); },
|
||||
true, true );
|
||||
} );
|
||||
msgBox->setCloseShortcut( { KEY_ESCAPE, KEYMOD_NONE } );
|
||||
msgBox->setTitle( i18n( "git_drop_stash_title", "Drop Stash" ) );
|
||||
msgBox->center();
|
||||
msgBox->showWhenReady();
|
||||
}
|
||||
|
||||
// Stash operations
|
||||
|
||||
void GitPlugin::onRegister( UICodeEditor* editor ) {
|
||||
PluginBase::onRegister( editor );
|
||||
|
||||
@@ -859,10 +972,10 @@ void GitPlugin::onRegister( UICodeEditor* editor ) {
|
||||
if ( mTab )
|
||||
mTab->setTabSelected();
|
||||
} );
|
||||
doc.setCommand( "git-pull", [this] { pull(); } );
|
||||
doc.setCommand( "git-push", [this] { push(); } );
|
||||
doc.setCommand( "git-fetch", [this] { fetch(); } );
|
||||
doc.setCommand( "git-commit", [this] { commit(); } );
|
||||
doc.setCommand( "git-pull", [this] { pull( projectPath() ); } );
|
||||
doc.setCommand( "git-push", [this] { push( projectPath() ); } );
|
||||
doc.setCommand( "git-fetch", [this] { fetch( projectPath() ); } );
|
||||
doc.setCommand( "git-commit", [this] { commit( projectPath() ); } );
|
||||
}
|
||||
|
||||
void GitPlugin::onUnregister( UICodeEditor* editor ) {
|
||||
@@ -1083,8 +1196,8 @@ void GitPlugin::buildSidePanelTab() {
|
||||
node->bind( "git_panel_loader", mLoader );
|
||||
node->bind( "git_repo", mRepoDropDown );
|
||||
|
||||
node->find( "branch_pull" )->onClick( [this]( auto ) { pull(); } );
|
||||
node->find( "branch_push" )->onClick( [this]( auto ) { push(); } );
|
||||
node->find( "branch_pull" )->onClick( [this]( auto ) { pull( repoSelected() ); } );
|
||||
node->find( "branch_push" )->onClick( [this]( auto ) { push( repoSelected() ); } );
|
||||
node->find( "branch_add" )->onClick( [this]( auto ) { branchCreate(); } );
|
||||
|
||||
mBranchesTree->setAutoExpandOnSingleColumn( true );
|
||||
@@ -1101,15 +1214,14 @@ void GitPlugin::buildSidePanelTab() {
|
||||
static_cast<Git::Branch*>( modelEvent->getModelIndex().internalData() );
|
||||
|
||||
switch ( modelEvent->getModelEventType() ) {
|
||||
case Abstract::ModelEventType::Open: {
|
||||
checkout( *branch );
|
||||
case ModelEventType::Open: {
|
||||
if ( branch->type != Git::RefType::Stash )
|
||||
checkout( *branch );
|
||||
else
|
||||
stashApply( *branch );
|
||||
break;
|
||||
}
|
||||
case Abstract::ModelEventType::OpenMenu: {
|
||||
bool focusOnSelection = mBranchesTree->getFocusOnSelection();
|
||||
mBranchesTree->setFocusOnSelection( false );
|
||||
mBranchesTree->getSelection().set( modelEvent->getModelIndex() );
|
||||
mBranchesTree->setFocusOnSelection( focusOnSelection );
|
||||
case ModelEventType::OpenMenu: {
|
||||
openBranchMenu( *branch );
|
||||
break;
|
||||
}
|
||||
@@ -1165,21 +1277,18 @@ void GitPlugin::buildSidePanelTab() {
|
||||
return;
|
||||
|
||||
auto model = static_cast<const GitStatusModel*>( modelEvent->getModel() );
|
||||
|
||||
if ( modelEvent->getModelIndex().internalId() == GitStatusModel::GitFile ) {
|
||||
const Git::DiffFile* file = model->file( modelEvent->getModelIndex() );
|
||||
if ( file == nullptr )
|
||||
return;
|
||||
|
||||
switch ( modelEvent->getModelEventType() ) {
|
||||
case Abstract::ModelEventType::OpenMenu: {
|
||||
bool focusOnSelection = mStatusTree->getFocusOnSelection();
|
||||
mStatusTree->setFocusOnSelection( false );
|
||||
mStatusTree->getSelection().set( modelEvent->getModelIndex() );
|
||||
mStatusTree->setFocusOnSelection( focusOnSelection );
|
||||
case ModelEventType::OpenMenu: {
|
||||
openFileStatusMenu( *file );
|
||||
break;
|
||||
}
|
||||
case Abstract::ModelEventType::Open: {
|
||||
case ModelEventType::Open: {
|
||||
diff( file->file, file->report.type == Git::GitStatusType::Staged );
|
||||
break;
|
||||
}
|
||||
@@ -1188,12 +1297,7 @@ void GitPlugin::buildSidePanelTab() {
|
||||
}
|
||||
} else if ( modelEvent->getModelIndex().internalId() == GitStatusModel::Status ) {
|
||||
switch ( modelEvent->getModelEventType() ) {
|
||||
case Abstract::ModelEventType::OpenMenu: {
|
||||
bool focusOnSelection = mStatusTree->getFocusOnSelection();
|
||||
mStatusTree->setFocusOnSelection( false );
|
||||
mStatusTree->getSelection().set( modelEvent->getModelIndex() );
|
||||
mStatusTree->setFocusOnSelection( focusOnSelection );
|
||||
|
||||
case ModelEventType::OpenMenu: {
|
||||
const auto* status = model->statusType( modelEvent->getModelIndex() );
|
||||
if ( status->type == Git::GitStatusType::Staged ||
|
||||
status->type == Git::GitStatusType::Untracked ||
|
||||
@@ -1224,11 +1328,11 @@ void GitPlugin::buildSidePanelTab() {
|
||||
if ( id == "git-commit" ) {
|
||||
commit( repoPath );
|
||||
} else if ( id == "git-stage-all" ) {
|
||||
stage( model->getFiles( mGit->repoName( "" ),
|
||||
stage( model->getFiles( repoName( repoPath ),
|
||||
(Uint32)Git::GitStatusType::Untracked |
|
||||
(Uint32)Git::GitStatusType::Changed ) );
|
||||
} else if ( id == "git-unstage-all" ) {
|
||||
unstage( model->getFiles( mGit->repoName( "" ),
|
||||
unstage( model->getFiles( repoName( repoPath ),
|
||||
(Uint32)Git::GitStatusType::Staged ) );
|
||||
}
|
||||
} );
|
||||
@@ -1241,6 +1345,65 @@ void GitPlugin::buildSidePanelTab() {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if ( modelEvent->getModelIndex().internalId() == GitStatusModel::Repo ) {
|
||||
switch ( modelEvent->getModelEventType() ) {
|
||||
case ModelEventType::OpenMenu: {
|
||||
const auto* repo = model->repo( modelEvent->getModelIndex() );
|
||||
if ( repo == nullptr )
|
||||
return;
|
||||
|
||||
std::string repoName = repo->repo;
|
||||
std::string repoPath = this->repoPath( repo->repo );
|
||||
if ( repoPath.empty() && !repo->type.empty() &&
|
||||
!repo->type.front().files.empty() )
|
||||
repoPath = mGit->repoPath( repo->type.front().files.front().file );
|
||||
|
||||
if ( repoPath.empty() )
|
||||
return;
|
||||
|
||||
UIPopUpMenu* menu = UIPopUpMenu::New();
|
||||
menu->setId( "git_repo_type_menu" );
|
||||
|
||||
if ( repo->hasStatusType( Git::GitStatusType::Staged ) ) {
|
||||
addMenuItem( menu, "git-commit", "Commit", "git-commit" );
|
||||
addMenuItem( menu, "git-stage-all", "Stage All" );
|
||||
}
|
||||
|
||||
addMenuItem( menu, "git-fetch", "Fetch", "repo-fetch" );
|
||||
addMenuItem( menu, "git-pull", "Pull", "repo-pull" );
|
||||
addMenuItem( menu, "git-push", "Push", "repo-push" );
|
||||
addMenuItem( menu, "git-stash", "Stash All", "git-stash" );
|
||||
|
||||
menu->on( Event::OnItemClicked,
|
||||
[this, model, repoName, repoPath]( const Event* event ) {
|
||||
if ( !mGit )
|
||||
return;
|
||||
UIMenuItem* item = event->getNode()->asType<UIMenuItem>();
|
||||
std::string id( item->getId() );
|
||||
if ( id == "git-stash" ) {
|
||||
stashPush( model->getFiles( repoName ), repoPath );
|
||||
} else if ( id == "git-fetch" ) {
|
||||
fetch( repoPath );
|
||||
} else if ( id == "git-pull" ) {
|
||||
pull( repoPath );
|
||||
} else if ( id == "git-push" ) {
|
||||
push( repoPath );
|
||||
} else if ( id == "git-commit" ) {
|
||||
commit( repoPath );
|
||||
} else if ( id == "git-stage-all" ) {
|
||||
stage( model->getFiles(
|
||||
repoName, (Uint32)Git::GitStatusType::Untracked |
|
||||
(Uint32)Git::GitStatusType::Changed ) );
|
||||
}
|
||||
} );
|
||||
|
||||
menu->showOverMouseCursor();
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
@@ -1264,25 +1427,30 @@ void GitPlugin::openBranchMenu( const Git::Branch& branch ) {
|
||||
UIPopUpMenu* menu = UIPopUpMenu::New();
|
||||
menu->setId( "git_branch_menu" );
|
||||
|
||||
addMenuItem( menu, "git-fetch", "Fetch", "repo-fetch" );
|
||||
if ( branch.type != Git::RefType::Stash ) {
|
||||
addMenuItem( menu, "git-fetch", "Fetch", "repo-fetch" );
|
||||
|
||||
if ( gitBranch() != branch.name ) {
|
||||
addMenuItem( menu, "git-checkout", "Check Out...", "git-fetch" );
|
||||
if ( gitBranch() != branch.name ) {
|
||||
addMenuItem( menu, "git-checkout", "Check Out...", "git-fetch" );
|
||||
}
|
||||
|
||||
if ( branch.type == Git::RefType::Head ) {
|
||||
addMenuItem( menu, "git-branch-rename", "Rename", "", { KEY_F2 } );
|
||||
addMenuItem( menu, "git-pull", "Pull", "repo-pull" );
|
||||
if ( branch.ahead )
|
||||
addMenuItem( menu, "git-push", "Push", "repo-push" );
|
||||
if ( branch.behind )
|
||||
addMenuItem( menu, "git-fast-forward-merge", "Fast Forward Merge" );
|
||||
menu->addSeparator();
|
||||
addMenuItem( menu, "git-branch-delete", "Delete", "remove" );
|
||||
}
|
||||
|
||||
addMenuItem( menu, "git-create-branch", "Create Branch", "repo-forked", { KEY_F7 } );
|
||||
} else {
|
||||
addMenuItem( menu, "git-stash-apply", "Apply Stash", "git-stash-apply" );
|
||||
addMenuItem( menu, "git-stash-drop", "Drop Stash", "git-stash-pop" );
|
||||
}
|
||||
|
||||
if ( branch.type == Git::RefType::Head ) {
|
||||
addMenuItem( menu, "git-branch-rename", "Rename", "", { KEY_F2 } );
|
||||
addMenuItem( menu, "git-pull", "Pull", "repo-pull" );
|
||||
if ( branch.ahead )
|
||||
addMenuItem( menu, "git-push", "Push", "repo-push" );
|
||||
if ( branch.behind )
|
||||
addMenuItem( menu, "git-fast-forward-merge", "Fast Forward Merge" );
|
||||
menu->addSeparator();
|
||||
addMenuItem( menu, "git-branch-delete", "Delete", "remove" );
|
||||
}
|
||||
|
||||
addMenuItem( menu, "git-create-branch", "Create Branch", "repo-forked", { KEY_F7 } );
|
||||
|
||||
menu->on( Event::OnItemClicked, [this, branch]( const Event* event ) {
|
||||
if ( !mGit )
|
||||
return;
|
||||
@@ -1291,19 +1459,23 @@ void GitPlugin::openBranchMenu( const Git::Branch& branch ) {
|
||||
if ( id == "git-checkout" ) {
|
||||
checkout( branch );
|
||||
} else if ( id == "git-pull" ) {
|
||||
pull();
|
||||
pull( repoSelected() );
|
||||
} else if ( id == "git-push" ) {
|
||||
push();
|
||||
push( repoSelected() );
|
||||
} else if ( id == "git-branch-delete" ) {
|
||||
branchDelete( branch );
|
||||
} else if ( id == "git-branch-rename" ) {
|
||||
branchRename( branch );
|
||||
} else if ( id == "git-fetch" ) {
|
||||
fetch();
|
||||
fetch( repoSelected() );
|
||||
} else if ( id == "git-fast-forward-merge" ) {
|
||||
fastForwardMerge( branch );
|
||||
} else if ( id == "git-create-branch" ) {
|
||||
branchCreate();
|
||||
} else if ( id == "git-stash-apply" ) {
|
||||
stashApply( branch );
|
||||
} else if ( id == "git-stash-drop" ) {
|
||||
stashDrop( branch );
|
||||
}
|
||||
} );
|
||||
|
||||
@@ -1314,8 +1486,8 @@ void GitPlugin::openFileStatusMenu( const Git::DiffFile& file ) {
|
||||
UIPopUpMenu* menu = UIPopUpMenu::New();
|
||||
menu->setId( "git_file_status_menu" );
|
||||
|
||||
addMenuItem( menu, "git-open-file", "Open File" );
|
||||
addMenuItem( menu, "git-diff", "Open Diff" );
|
||||
addMenuItem( menu, "git-open-file", "Open File", "file" );
|
||||
addMenuItem( menu, "git-diff", "Open Diff", "diff-single" );
|
||||
|
||||
if ( file.report.type != Git::GitStatusType::Staged ) {
|
||||
addMenuItem( menu, "git-stage", "Stage" );
|
||||
@@ -1387,6 +1559,11 @@ std::string GitPlugin::repoSelected() {
|
||||
return mRepoSelected;
|
||||
}
|
||||
|
||||
std::string GitPlugin::projectPath() {
|
||||
Lock l( mRepoMutex );
|
||||
return mProjectPath;
|
||||
}
|
||||
|
||||
std::string GitPlugin::repoName( const std::string& repoPath ) {
|
||||
Lock l( mRepoMutex );
|
||||
for ( const auto& repo : mRepos )
|
||||
@@ -1395,4 +1572,12 @@ std::string GitPlugin::repoName( const std::string& repoPath ) {
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string GitPlugin::repoPath( const std::string& repoName ) {
|
||||
Lock l( mRepoMutex );
|
||||
for ( const auto& repo : mRepos )
|
||||
if ( repo.second == repoName )
|
||||
return repo.first;
|
||||
return "";
|
||||
}
|
||||
|
||||
} // namespace ecode
|
||||
|
||||
@@ -31,6 +31,7 @@ static constexpr const char* GIT_BOLD = "bold";
|
||||
static constexpr const char* GIT_NOT_BOLD = "notbold";
|
||||
static constexpr const char* GIT_TAG = "tag";
|
||||
static constexpr const char* GIT_REPO = "repo";
|
||||
static constexpr const char* GIT_STASH = "git-stash";
|
||||
|
||||
class GitPlugin : public PluginBase {
|
||||
public:
|
||||
@@ -154,15 +155,15 @@ class GitPlugin : public PluginBase {
|
||||
|
||||
void fastForwardMerge( Git::Branch branch );
|
||||
|
||||
void pull();
|
||||
void pull( const std::string& repoPath );
|
||||
|
||||
void push();
|
||||
void push( const std::string& repoPath );
|
||||
|
||||
void fetch();
|
||||
void fetch( const std::string& repoPath );
|
||||
|
||||
void branchCreate();
|
||||
|
||||
void commit( const std::string& repoPath = "" );
|
||||
void commit( const std::string& repoPath );
|
||||
|
||||
void stage( const std::vector<std::string>& files );
|
||||
|
||||
@@ -192,6 +193,12 @@ class GitPlugin : public PluginBase {
|
||||
|
||||
void openFileStatusMenu( const Git::DiffFile& file );
|
||||
|
||||
void stashPush( const std::vector<std::string>& files, const std::string& repoPath );
|
||||
|
||||
void stashApply( const Git::Branch& branch );
|
||||
|
||||
void stashDrop( const Git::Branch& branch );
|
||||
|
||||
void runAsync( std::function<Git::Result()> fn, bool updateStatus, bool updateBranches,
|
||||
bool displaySuccessMsg = false, bool updateBranchesOnError = false );
|
||||
|
||||
@@ -201,8 +208,12 @@ class GitPlugin : public PluginBase {
|
||||
|
||||
std::string repoSelected();
|
||||
|
||||
std::string projectPath();
|
||||
|
||||
std::string repoName( const std::string& repoPath );
|
||||
|
||||
std::string repoPath( const std::string& repoName );
|
||||
|
||||
std::string fixFilePath( const std::string& file );
|
||||
|
||||
std::vector<std::string> fixFilePaths( const std::vector<std::string>& files );
|
||||
|
||||
@@ -38,6 +38,14 @@ class GitStatusModel : public Model {
|
||||
struct RepoStatus {
|
||||
std::string repo;
|
||||
std::vector<RepoStatusType> type;
|
||||
|
||||
bool hasStatusType( Git::GitStatusType statusType ) const {
|
||||
for ( const auto& t : type ) {
|
||||
if ( t.type == statusType )
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
enum Column { File, State, Inserted, Removed, RelativeDirectory };
|
||||
@@ -66,7 +74,8 @@ class GitStatusModel : public Model {
|
||||
|
||||
const DiffFile* file( const ModelIndex& index ) const;
|
||||
|
||||
std::vector<std::string> getFiles( const std::string& repo, uint32_t statusType ) const;
|
||||
std::vector<std::string> getFiles( const std::string& repo,
|
||||
uint32_t statusType = Git::STATUS_TYPE_ALL ) const;
|
||||
|
||||
protected:
|
||||
std::vector<RepoStatus> mStatus;
|
||||
|
||||
Reference in New Issue
Block a user