More Git Plugin WIP. Added more operations.

This commit is contained in:
Martín Lucas Golini
2024-01-20 14:18:27 -03:00
parent b5f660ca21
commit f35cc3d034
4 changed files with 208 additions and 72 deletions

View File

@@ -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 );

View File

@@ -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 = "" );

View File

@@ -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();

View File

@@ -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();