mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-08-14 13:15:14 +03:00
More Git Plugin WIP. Added more operations.
This commit is contained in:
@@ -154,9 +154,40 @@ Git::CheckoutResult Git::checkout( const std::string& branch,
|
||||
return res;
|
||||
}
|
||||
|
||||
Git::CheckoutResult Git::checkoutAndCreateLocalBranch( const std::string& remoteBranch,
|
||||
const std::string& newBranch,
|
||||
const std::string& projectDir ) const {
|
||||
std::string newBranchName =
|
||||
newBranch.empty() ? ( remoteBranch.find_last_of( '/' ) != std::string::npos
|
||||
? remoteBranch.substr( remoteBranch.find_last_of( '/' ) + 1 )
|
||||
: remoteBranch )
|
||||
: newBranch;
|
||||
Git::CheckoutResult res;
|
||||
std::string buf;
|
||||
int retCode =
|
||||
git( String::format( "branch --no-track %s refs/remotes/%s", newBranchName, remoteBranch ),
|
||||
projectDir, buf );
|
||||
if ( retCode != EXIT_SUCCESS ) {
|
||||
res.returnCode = retCode;
|
||||
res.result = buf;
|
||||
return res;
|
||||
}
|
||||
|
||||
retCode = git( String::format( "branch --set-upstream-to=refs/remotes/%s %s", remoteBranch,
|
||||
newBranchName ),
|
||||
projectDir, buf );
|
||||
if ( retCode != EXIT_SUCCESS ) {
|
||||
res.returnCode = retCode;
|
||||
res.result = buf;
|
||||
return res;
|
||||
}
|
||||
|
||||
return checkout( newBranchName, projectDir );
|
||||
}
|
||||
|
||||
Git::Result Git::add( const std::string& file, const std::string& projectDir ) {
|
||||
std::string buf;
|
||||
int retCode = git( String::format( "add --force -- %s", file ), projectDir, buf );
|
||||
int retCode = git( String::format( "add --force -- \"%s\"", file ), projectDir, buf );
|
||||
Git::CheckoutResult res;
|
||||
res.returnCode = retCode;
|
||||
res.result = buf;
|
||||
@@ -165,7 +196,7 @@ Git::Result Git::add( const std::string& file, const std::string& projectDir ) {
|
||||
|
||||
Git::Result Git::restore( const std::string& file, const std::string& projectDir ) {
|
||||
std::string buf;
|
||||
int retCode = git( String::format( "restore %s", file ), projectDir, buf );
|
||||
int retCode = git( String::format( "restore \"%s\"", file ), projectDir, buf );
|
||||
Git::CheckoutResult res;
|
||||
res.returnCode = retCode;
|
||||
res.result = buf;
|
||||
@@ -174,13 +205,58 @@ Git::Result Git::restore( const std::string& file, const std::string& projectDir
|
||||
|
||||
Git::Result Git::reset( const std::string& file, const std::string& projectDir ) {
|
||||
std::string buf;
|
||||
int retCode = git( String::format( "reset -q HEAD -- %s", file ), projectDir, buf );
|
||||
int retCode = git( String::format( "reset -q HEAD -- \"%s\"", file ), projectDir, buf );
|
||||
Git::CheckoutResult res;
|
||||
res.returnCode = retCode;
|
||||
res.result = buf;
|
||||
return res;
|
||||
}
|
||||
|
||||
Git::Result Git::deleteBranch( const std::string& branch, const std::string& projectDir ) {
|
||||
std::string buf;
|
||||
int retCode = git( String::format( "branch -D %s", branch ), projectDir, buf );
|
||||
Git::CheckoutResult res;
|
||||
res.returnCode = retCode;
|
||||
res.result = buf;
|
||||
return res;
|
||||
}
|
||||
|
||||
Git::CountResult Git::branchHistoryPosition( const std::string& localBranch,
|
||||
const std::string& remoteBranch,
|
||||
const std::string& projectDir ) {
|
||||
std::string buf;
|
||||
int retCode = git( String::format( "rev-list --count %s..%s", localBranch, remoteBranch ),
|
||||
projectDir, buf );
|
||||
Git::CountResult res;
|
||||
res.returnCode = retCode;
|
||||
if ( res.success() ) {
|
||||
uint64_t count = 0;
|
||||
if ( String::fromString( count, buf ) )
|
||||
res.behind = count;
|
||||
} else {
|
||||
res.result = buf;
|
||||
return res;
|
||||
}
|
||||
|
||||
retCode = git( String::format( "rev-list --count %s..%s", remoteBranch, localBranch ),
|
||||
projectDir, buf );
|
||||
res.returnCode = retCode;
|
||||
|
||||
if ( res.success() ) {
|
||||
uint64_t count = 0;
|
||||
if ( String::fromString( count, buf ) )
|
||||
res.ahead = count;
|
||||
} else {
|
||||
res.result = buf;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
Git::CountResult Git::branchHistoryPosition( const Branch& branch, const std::string& projectDir ) {
|
||||
return branchHistoryPosition( branch.name, branch.remote, projectDir );
|
||||
}
|
||||
|
||||
Git::CheckoutResult Git::checkoutNewBranch( const std::string& newBranch,
|
||||
const std::string& fromBranch,
|
||||
const std::string& projectDir ) {
|
||||
@@ -305,20 +381,18 @@ Git::Status Git::status( bool recurseSubmodules, const std::string& projectDir )
|
||||
LuaPattern subModulePattern( "^Entering '(.*)'" );
|
||||
|
||||
auto parseNumStat = [&s, &buf, &projectDir, this, &subModulePattern]() {
|
||||
auto lastNL = 0;
|
||||
auto nextNL = buf.find_first_of( '\n' );
|
||||
LuaPattern pattern( "(%d+)%s+(%d+)%s+(.+)" );
|
||||
std::string subModulePath = "";
|
||||
while ( nextNL != std::string_view::npos ) {
|
||||
readAllLines( buf, [&]( const std::string_view& line ) {
|
||||
LuaPattern::Range matches[4];
|
||||
if ( subModulePattern.matches( buf.c_str(), lastNL, matches, nextNL ) ) {
|
||||
if ( subModulePattern.matches( line.data(), 0, matches, line.size() ) ) {
|
||||
subModulePath = String::trim(
|
||||
buf.substr( matches[1].start, matches[1].end - matches[1].start ) );
|
||||
line.substr( matches[1].start, matches[1].end - matches[1].start ) );
|
||||
FileSystem::dirAddSlashAtEnd( subModulePath );
|
||||
} else if ( pattern.matches( buf.c_str(), lastNL, matches, nextNL ) ) {
|
||||
auto inserted = buf.substr( matches[1].start, matches[1].end - matches[1].start );
|
||||
auto deleted = buf.substr( matches[2].start, matches[2].end - matches[2].start );
|
||||
auto file = buf.substr( matches[3].start, matches[3].end - matches[3].start );
|
||||
} else if ( pattern.matches( line.data(), 0, matches, line.size() ) ) {
|
||||
auto inserted = line.substr( matches[1].start, matches[1].end - matches[1].start );
|
||||
auto deleted = line.substr( matches[2].start, matches[2].end - matches[2].start );
|
||||
auto file = line.substr( matches[3].start, matches[3].end - matches[3].start );
|
||||
int inserts;
|
||||
int deletes;
|
||||
if ( String::fromString( inserts, inserted ) &&
|
||||
@@ -351,9 +425,7 @@ Git::Status Git::status( bool recurseSubmodules, const std::string& projectDir )
|
||||
s.totalDeletions += deletes;
|
||||
}
|
||||
}
|
||||
lastNL = nextNL + 1;
|
||||
nextNL = buf.find_first_of( '\n', nextNL + 1 );
|
||||
}
|
||||
} );
|
||||
};
|
||||
|
||||
parseNumStat();
|
||||
@@ -373,12 +445,9 @@ Git::Status Git::status( bool recurseSubmodules, const std::string& projectDir )
|
||||
|
||||
bool modifiedSubmodule = false;
|
||||
auto parseStatus = [&s, &buf, &modifiedSubmodule, &projectDir, this, &subModulePattern]() {
|
||||
auto lastNL = 0;
|
||||
auto nextNL = buf.find_first_of( '\n' );
|
||||
LuaPattern pattern( "^([mMARTUD?%s][mMARTUD?%s])%s(.*)" );
|
||||
std::string subModulePath = "";
|
||||
while ( nextNL != std::string_view::npos ) {
|
||||
std::string_view line = std::string_view{ buf }.substr( lastNL, nextNL - lastNL );
|
||||
LuaPattern pattern( "^([mMARTUD?%s][mMARTUD?%s])%s(.*)" );
|
||||
readAllLines( buf, [&]( const std::string_view& line ) {
|
||||
LuaPattern::Range matches[3];
|
||||
if ( subModulePattern.matches( line.data(), 0, matches, line.size() ) ) {
|
||||
subModulePath = String::trim(
|
||||
@@ -388,7 +457,7 @@ Git::Status Git::status( bool recurseSubmodules, const std::string& projectDir )
|
||||
auto statusStr = line.substr( matches[1].start, matches[1].end - matches[1].start );
|
||||
auto file = line.substr( matches[2].start, matches[2].end - matches[2].start );
|
||||
if ( statusStr.size() < 2 )
|
||||
continue;
|
||||
return;
|
||||
|
||||
Uint16 status = xy( statusStr[0], statusStr[1] );
|
||||
GitStatus gitStatus = GitStatus::NotSet;
|
||||
@@ -557,9 +626,7 @@ Git::Status Git::status( bool recurseSubmodules, const std::string& projectDir )
|
||||
}
|
||||
}
|
||||
}
|
||||
lastNL = nextNL + 1;
|
||||
nextNL = buf.find_first_of( '\n', nextNL + 1 );
|
||||
}
|
||||
} );
|
||||
};
|
||||
|
||||
git( STATUS_CMD, projectDir, buf );
|
||||
|
||||
@@ -147,6 +147,11 @@ class Git {
|
||||
std::string branch;
|
||||
};
|
||||
|
||||
struct CountResult : public Result {
|
||||
int64_t behind{ 0 };
|
||||
int64_t ahead{ 0 };
|
||||
};
|
||||
|
||||
enum RefType {
|
||||
Head = 0x1,
|
||||
Remote = 0x2,
|
||||
@@ -205,6 +210,15 @@ class Git {
|
||||
|
||||
Result reset( const std::string& file, const std::string& projectDir = "" );
|
||||
|
||||
Result deleteBranch( const std::string& branch, const std::string& projectDir = "" );
|
||||
|
||||
CountResult branchHistoryPosition( const std::string& localBranch,
|
||||
const std::string& remoteBranch,
|
||||
const std::string& projectDir = "" );
|
||||
|
||||
CountResult branchHistoryPosition( const Git::Branch& branch,
|
||||
const std::string& projectDir = "" );
|
||||
|
||||
bool setProjectPath( const std::string& projectPath );
|
||||
|
||||
const std::string& getGitPath() const;
|
||||
@@ -219,6 +233,10 @@ class Git {
|
||||
|
||||
CheckoutResult checkout( const std::string& branch, const std::string& projectDir = "" ) const;
|
||||
|
||||
CheckoutResult checkoutAndCreateLocalBranch( const std::string& remoteBranch,
|
||||
const std::string& newBranch = "",
|
||||
const std::string& projectDir = "" ) const;
|
||||
|
||||
CheckoutResult checkoutNewBranch( const std::string& newBranch,
|
||||
const std::string& fromBranch = "",
|
||||
const std::string& projectDir = "" );
|
||||
|
||||
@@ -29,8 +29,8 @@ static const char* GIT_SUCCESS = "success";
|
||||
static const char* GIT_ERROR = "error";
|
||||
static const char* GIT_BOLD = "bold";
|
||||
static const char* GIT_NOT_BOLD = "notbold";
|
||||
static const std::string GIT_TAG = "tag";
|
||||
static const std::string GIT_REPO = "repo";
|
||||
static const char* GIT_TAG = "tag";
|
||||
static const char* GIT_REPO = "repo";
|
||||
|
||||
std::string GitPlugin::statusTypeToString( Git::GitStatusType type ) {
|
||||
switch ( type ) {
|
||||
@@ -140,7 +140,8 @@ class GitBranchModel : public Model {
|
||||
case ModelRole::Display: {
|
||||
if ( index.internalId() == -1 ) {
|
||||
if ( index.column() == Column::Name )
|
||||
return Variant( mBranches[index.row()].branch.c_str() );
|
||||
return Variant( String::format( "%s (%zu)", mBranches[index.row()].branch,
|
||||
mBranches[index.row()].data.size() ) );
|
||||
return Variant( GIT_EMPTY );
|
||||
}
|
||||
const Git::Branch& branch = mBranches[index.internalId()].data[index.row()];
|
||||
@@ -843,22 +844,58 @@ void GitPlugin::blame( UICodeEditor* editor ) {
|
||||
} );
|
||||
}
|
||||
|
||||
void GitPlugin::checkout( const std::string& branch ) {
|
||||
void GitPlugin::checkout( Git::Branch branch ) {
|
||||
if ( !mGit )
|
||||
return;
|
||||
|
||||
const auto checkOutFn = [this, branch]( bool createLocal ) {
|
||||
mLoader->setVisible( true );
|
||||
mThreadPool->run( [this, branch, createLocal] {
|
||||
auto result = createLocal ? mGit->checkoutAndCreateLocalBranch( branch.name )
|
||||
: mGit->checkout( branch.name );
|
||||
if ( result.success() ) {
|
||||
{
|
||||
Lock l( mGitBranchMutex );
|
||||
mGitBranch = branch.name;
|
||||
}
|
||||
if ( mBranchesTree->getModel() ) {
|
||||
if ( createLocal )
|
||||
updateBranches();
|
||||
else
|
||||
mBranchesTree->getModel()->invalidate( Model::DontInvalidateIndexes );
|
||||
}
|
||||
} else {
|
||||
showMessage( LSPMessageType::Warning, result.result );
|
||||
}
|
||||
getUISceneNode()->runOnMainThread( [this] { mLoader->setVisible( false ); } );
|
||||
} );
|
||||
};
|
||||
|
||||
if ( branch.type == Git::RefType::Remote ) {
|
||||
UIMessageBox* msgBox = UIMessageBox::New(
|
||||
UIMessageBox::YES_NO, i18n( "git_create_local_branch", "Create local branch?" ) );
|
||||
msgBox->on( Event::OnConfirm, [checkOutFn]( const Event* ) { checkOutFn( true ); } );
|
||||
msgBox->on( Event::OnCancel, [checkOutFn]( const Event* ) { checkOutFn( false ); } );
|
||||
msgBox->setTitle( i18n( "git_checkout", "Check Out" ) );
|
||||
msgBox->center();
|
||||
msgBox->showWhenReady();
|
||||
return;
|
||||
}
|
||||
|
||||
checkOutFn( false );
|
||||
}
|
||||
|
||||
void GitPlugin::branchDelete( Git::Branch branch ) {
|
||||
if ( !mGit )
|
||||
return;
|
||||
mLoader->setVisible( true );
|
||||
mThreadPool->run( [this, branch] {
|
||||
auto result = mGit->checkout( branch );
|
||||
if ( result.success() ) {
|
||||
{
|
||||
Lock l( mGitBranchMutex );
|
||||
mGitBranch = branch;
|
||||
}
|
||||
if ( mBranchesTree->getModel() )
|
||||
mBranchesTree->getModel()->invalidate( Model::DontInvalidateIndexes );
|
||||
} else {
|
||||
showMessage( LSPMessageType::Warning, result.result );
|
||||
auto res = mGit->deleteBranch( branch.name );
|
||||
if ( res.fail() ) {
|
||||
showMessage( LSPMessageType::Warning, res.result );
|
||||
return;
|
||||
}
|
||||
updateBranches();
|
||||
getUISceneNode()->runOnMainThread( [this] { mLoader->setVisible( false ); } );
|
||||
} );
|
||||
}
|
||||
@@ -904,14 +941,27 @@ void GitPlugin::unstage( const std::string& file ) {
|
||||
void GitPlugin::discard( const std::string& file ) {
|
||||
if ( !mGit )
|
||||
return;
|
||||
mLoader->setVisible( true );
|
||||
mThreadPool->run( [this, file] {
|
||||
auto res = mGit->restore( file );
|
||||
if ( res.fail() )
|
||||
showMessage( LSPMessageType::Warning, res.result );
|
||||
getUISceneNode()->runOnMainThread( [this] { mLoader->setVisible( false ); } );
|
||||
updateStatus( true );
|
||||
|
||||
UIMessageBox* msgBox = UIMessageBox::New(
|
||||
UIMessageBox::OK_CANCEL,
|
||||
String::format( i18n( "git_confirm_discard_changes",
|
||||
"Are you sure you want to discard the changes in file: \"%s\"?" )
|
||||
.toUtf8(),
|
||||
file ) );
|
||||
|
||||
msgBox->on( Event::OnConfirm, [this, file]( auto ) {
|
||||
mLoader->setVisible( true );
|
||||
mThreadPool->run( [this, file] {
|
||||
auto res = mGit->restore( file );
|
||||
if ( res.fail() )
|
||||
showMessage( LSPMessageType::Warning, res.result );
|
||||
getUISceneNode()->runOnMainThread( [this] { mLoader->setVisible( false ); } );
|
||||
updateStatus( true );
|
||||
} );
|
||||
} );
|
||||
msgBox->setTitle( i18n( "git_confirm", "Confirm" ) );
|
||||
msgBox->center();
|
||||
msgBox->showWhenReady();
|
||||
}
|
||||
|
||||
void GitPlugin::openFile( const std::string& file ) {
|
||||
@@ -940,7 +990,6 @@ void GitPlugin::onRegister( UICodeEditor* editor ) {
|
||||
mTab->setTabSelected();
|
||||
} );
|
||||
doc.setCommand( "git-pull", [this]() { pull(); } );
|
||||
doc.setCommand( "git-checkout", [this]() { checkout( mGitBranch ); } );
|
||||
}
|
||||
|
||||
void GitPlugin::onUnregister( UICodeEditor* editor ) {
|
||||
@@ -1082,7 +1131,7 @@ void GitPlugin::buildSidePanelTab() {
|
||||
|
||||
switch ( modelEvent->getModelEventType() ) {
|
||||
case EE::UI::Abstract::ModelEventType::Open: {
|
||||
checkout( branch->name );
|
||||
checkout( *branch );
|
||||
break;
|
||||
}
|
||||
case EE::UI::Abstract::ModelEventType::OpenMenu: {
|
||||
@@ -1146,23 +1195,25 @@ void GitPlugin::openBranchMenu( const Git::Branch& branch ) {
|
||||
|
||||
if ( mGitBranch != branch.name ) {
|
||||
addFn( "git-checkout", "Check Out..." );
|
||||
if ( branch.type == Git::RefType::Head ) {
|
||||
addFn( "git-branch-delete", "Delete" );
|
||||
}
|
||||
} else {
|
||||
addFn( "git-pull", "Pull", "repo-pull" );
|
||||
}
|
||||
|
||||
std::string name( branch.name );
|
||||
menu->on( Event::OnItemClicked, [this, name]( const Event* event ) {
|
||||
menu->on( Event::OnItemClicked, [this, branch]( const Event* event ) {
|
||||
if ( !mGit )
|
||||
return;
|
||||
UIMenuItem* item = event->getNode()->asType<UIMenuItem>();
|
||||
std::string id( item->getId() );
|
||||
mThreadPool->run( [this, id, name]() {
|
||||
if ( id == "git-checkout" ) {
|
||||
checkout( name );
|
||||
} else if ( id == "git-pull" ) {
|
||||
pull();
|
||||
}
|
||||
} );
|
||||
if ( id == "git-checkout" ) {
|
||||
checkout( branch );
|
||||
} else if ( id == "git-pull" ) {
|
||||
pull();
|
||||
} else if ( id == "git-branch-delete" ) {
|
||||
branchDelete( branch );
|
||||
}
|
||||
} );
|
||||
|
||||
menu->showOverMouseCursor();
|
||||
@@ -1185,28 +1236,26 @@ void GitPlugin::openFileStatusMenu( const Git::DiffFile& file ) {
|
||||
addFn( "git-stage", "Stage" );
|
||||
} else {
|
||||
addFn( "git-unstage", "Unstage" );
|
||||
|
||||
menu->addSeparator();
|
||||
|
||||
addFn( "git-discard", "Discard" );
|
||||
}
|
||||
|
||||
menu->addSeparator();
|
||||
|
||||
addFn( "git-discard", "Discard" );
|
||||
|
||||
menu->on( Event::OnItemClicked, [this, file]( const Event* event ) {
|
||||
if ( !mGit )
|
||||
return;
|
||||
UIMenuItem* item = event->getNode()->asType<UIMenuItem>();
|
||||
std::string id( item->getId() );
|
||||
mThreadPool->run( [this, id, file]() {
|
||||
if ( id == "git-stage" ) {
|
||||
stage( file.file );
|
||||
} else if ( id == "git-unstage" ) {
|
||||
unstage( file.file );
|
||||
} else if ( id == "git-discard" ) {
|
||||
discard( file.file );
|
||||
} else if ( id == "git-open-file" ) {
|
||||
openFile( file.file );
|
||||
}
|
||||
} );
|
||||
if ( id == "git-stage" ) {
|
||||
stage( file.file );
|
||||
} else if ( id == "git-unstage" ) {
|
||||
unstage( file.file );
|
||||
} else if ( id == "git-discard" ) {
|
||||
discard( file.file );
|
||||
} else if ( id == "git-open-file" ) {
|
||||
openFile( file.file );
|
||||
}
|
||||
} );
|
||||
|
||||
menu->showOverMouseCursor();
|
||||
|
||||
@@ -120,7 +120,9 @@ class GitPlugin : public PluginBase {
|
||||
|
||||
void blame( UICodeEditor* editor );
|
||||
|
||||
void checkout( const std::string& branch );
|
||||
void checkout( Git::Branch branch );
|
||||
|
||||
void branchDelete( Git::Branch branch );
|
||||
|
||||
void pull();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user