diff --git a/src/tools/ecode/plugins/git/git.cpp b/src/tools/ecode/plugins/git/git.cpp index 4c06534db..b7c0d66a6 100644 --- a/src/tools/ecode/plugins/git/git.cpp +++ b/src/tools/ecode/plugins/git/git.cpp @@ -225,31 +225,27 @@ 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 ); + int retCode = + git( String::format( "rev-list --left-right --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; + String::trimInPlace( buf ); + auto results = String::split( buf, '\t' ); + if ( results.size() == 2 ) { + int64_t behind = 0; + int64_t ahead = 0; + if ( String::fromString( ahead, results[0] ) && + String::fromString( behind, results[1] ) ) { + res.ahead = ahead; + res.behind = behind; + } + } } 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; } @@ -277,12 +273,28 @@ std::vector Git::getAllBranches( const std::string& projectDir ) { projectDir ); } -static Git::Branch parseLocalBranch( const std::string_view& raw ) { +Git::Branch Git::parseLocalBranch( const std::string_view& raw, + const std::unordered_map& headOrigins, + const std::string& projectDir ) { static constexpr size_t len = std::string_view{ "refs/heads/"sv }.size(); - if ( len < raw.size() ) - return Git::Branch{ std::string{ raw.substr( len ) }, std::string{}, Git::RefType::Head, - std::string{} }; - return {}; + if ( len >= raw.size() ) + return {}; + + std::string name( std::string{ raw.substr( len ) } ); + auto found = headOrigins.find( name ); + std::string remote; + int64_t ahead = 0; + int64_t behind = 0; + if ( found != headOrigins.end() ) { + remote = found->second; + auto res = branchHistoryPosition( name, remote ); + if ( res.success() ) { + ahead = res.ahead; + behind = res.behind; + } + } + return Git::Branch{ + std::move( name ), std::move( remote ), Git::RefType::Head, std::string{}, ahead, behind }; } static Git::Branch parseRemoteBranch( const std::string_view& raw ) { @@ -313,10 +325,12 @@ std::vector Git::getAllBranchesAndTags( RefType ref, const std::str branches.reserve( countLines( buf ) ); - readAllLines( buf, [&branches, ref]( const std::string_view& line ) { + auto headOrigins = getHeadOrigins( projectDir ); + + readAllLines( buf, [&branches, ref, &headOrigins, this]( const std::string_view& line ) { auto branch = String::trim( String::trim( line, '\n' ), '\'' ); if ( ( ref & Head ) && String::startsWith( branch, "refs/heads/" ) ) { - branches.emplace_back( parseLocalBranch( branch ) ); + branches.emplace_back( parseLocalBranch( branch, headOrigins ) ); } else if ( ( ref & Remote ) && String::startsWith( branch, "refs/remotes/" ) ) { branches.emplace_back( parseRemoteBranch( branch ) ); } else if ( ( ref & Tag ) && String::startsWith( branch, "refs/tags/" ) ) { @@ -354,6 +368,26 @@ std::vector Git::getSubModules( const std::string& projectDir ) { return mSubModules; } +std::unordered_map Git::getHeadOrigins( const std::string& projectDir ) { + std::unordered_map ret; + std::string buf; + int retCode = git( "for-each-ref --format='%(refname:short) %(upstream:short)' refs/heads", + projectDir, buf ); + Result res; + res.returnCode = retCode; + if ( res.fail() ) + return ret; + + readAllLines( buf, [&ret]( const std::string_view& line ) { + auto split = String::split( String::trim( line, '\'' ), '\t' ); + if ( split.size() != 2 ) + return; + ret[std::string{ split[0] }] = std::string{ split[1] }; + } ); + + return ret; +} + bool Git::hasSubmodules( const std::string& projectDir ) { return ( !projectDir.empty() && FileSystem::fileExists( projectDir + ".gitmodules" ) ) || ( !mProjectPath.empty() && FileSystem::fileExists( mProjectPath + ".gitmodules" ) ); @@ -459,7 +493,7 @@ Git::Status Git::status( bool recurseSubmodules, const std::string& projectDir ) if ( statusStr.size() < 2 ) return; - Uint16 status = xy( statusStr[0], statusStr[1] ); + Uint16 status = git_xy( statusStr[0], statusStr[1] ); GitStatus gitStatus = GitStatus::NotSet; GitStatusChar gitStatusChar = GitStatusChar::Unknown; GitStatusType gitStatusType = GitStatusType::Untracked; diff --git a/src/tools/ecode/plugins/git/git.hpp b/src/tools/ecode/plugins/git/git.hpp index e4b235693..9fe975e3a 100644 --- a/src/tools/ecode/plugins/git/git.hpp +++ b/src/tools/ecode/plugins/git/git.hpp @@ -5,6 +5,8 @@ namespace ecode { +#define git_xy( x, y ) ( ( (uint16_t)x ) << 8 | y ) + class Git { public: struct Blame { @@ -72,37 +74,29 @@ class Git { Ignored, }; - static constexpr uint16_t xy( char x, char y ) { return ( (uint16_t)x ) << 8 | y; } - - static constexpr uint16_t x( uint16_t val ) { return ( val >> 8 ) & 0xFF; } - - static constexpr uint16_t y( uint16_t val ) { return val & 0xFF; } - static constexpr auto ANY = 0; -#define _xy( x, y ) ( ( (uint16_t)x ) << 8 | y ) - enum StatusXY : uint16_t { - A = _xy( ' ', 'A' ), // modified, added not updated - M = _xy( ' ', 'M' ), // modified, modified not updated - D = _xy( ' ', 'D' ), // modified, deleted not updated - m = _xy( ' ', 'm' ), // modified, modified submodule not updated - DD = _xy( 'D', 'D' ), // unmerged, both deleted - AU = _xy( 'A', 'U' ), // unmerged, added by us - UD = _xy( 'U', 'D' ), // unmerged, deleted by them - UA = _xy( 'U', 'A' ), // unmerged, added by them - DU = _xy( 'D', 'U' ), // unmerged, deleted by us - AA = _xy( 'A', 'A' ), // unmerged, both added - UU = _xy( 'U', 'U' ), // unmerged, both modified - QQ = _xy( '?', '?' ), // untracked - II = _xy( '!', '!' ), // ignored - SM = _xy( 'M', ANY ), // staged modified - ST = _xy( 'T', ANY ), // staged type changed - SA = _xy( 'A', ANY ), // staged added - SD = _xy( 'D', ANY ), // staged deleted - SR = _xy( 'R', ANY ), // staged renamed - SC = _xy( 'C', ANY ), // staged copied - SMM = _xy( 'm', ANY ), // staged modified submodule + A = git_xy( ' ', 'A' ), // modified, added not updated + M = git_xy( ' ', 'M' ), // modified, modified not updated + D = git_xy( ' ', 'D' ), // modified, deleted not updated + m = git_xy( ' ', 'm' ), // modified, modified submodule not updated + DD = git_xy( 'D', 'D' ), // unmerged, both deleted + AU = git_xy( 'A', 'U' ), // unmerged, added by us + UD = git_xy( 'U', 'D' ), // unmerged, deleted by them + UA = git_xy( 'U', 'A' ), // unmerged, added by them + DU = git_xy( 'D', 'U' ), // unmerged, deleted by us + AA = git_xy( 'A', 'A' ), // unmerged, both added + UU = git_xy( 'U', 'U' ), // unmerged, both modified + QQ = git_xy( '?', '?' ), // untracked + II = git_xy( '!', '!' ), // ignored + SM = git_xy( 'M', ANY ), // staged modified + ST = git_xy( 'T', ANY ), // staged type changed + SA = git_xy( 'A', ANY ), // staged added + SD = git_xy( 'D', ANY ), // staged deleted + SR = git_xy( 'R', ANY ), // staged renamed + SC = git_xy( 'C', ANY ), // staged copied + SMM = git_xy( 'm', ANY ), // staged modified submodule }; struct DiffFile { @@ -186,6 +180,9 @@ class Git { RefType type = All; /** last commit on this branch, may be empty **/ std::string lastCommit; + /** if it's HEAD how much ahead and behind the current local branch is against remote */ + int64_t ahead{ 0 }; + int64_t behind{ 0 }; const char* typeStr() const { return refTypeToString( type ); } }; @@ -256,6 +253,9 @@ class Git { std::vector getSubModules( const std::string& projectDir = "" ); + std::unordered_map + getHeadOrigins( const std::string& projectDir = "" ); + protected: std::string mGitPath; std::string mProjectPath; @@ -267,6 +267,10 @@ class Git { bool hasSubmodules( const std::string& projectDir ); std::string inSubModule( const std::string& file, const std::string& projectDir ); + + Git::Branch parseLocalBranch( const std::string_view& raw, + const std::unordered_map& headOrigins, + const std::string& projectDir = "" ); }; } // namespace ecode