Improve git status parsing.

This commit is contained in:
Martín Lucas Golini
2024-01-17 21:56:09 -03:00
parent 06933e1cfc
commit ff40471a45
3 changed files with 240 additions and 35 deletions
+163 -31
View File
@@ -305,10 +305,16 @@ Git::Status Git::status( bool recurseSubmodules, const std::string& projectDir )
break;
}
}
if ( !found )
s.files[repo].push_back( { std::move( filePath ), inserts, deletes } );
if ( !found ) {
s.files[repo].push_back( { std::move( filePath ), inserts, deletes,
GitStatus::NotSet, GitStatusType::Untracked,
GitStatusChar::Untracked } );
}
} else {
s.files.insert( { repo, { { std::move( filePath ), inserts, deletes } } } );
s.files.insert(
{ repo,
{ { std::move( filePath ), inserts, deletes, GitStatus::NotSet,
GitStatusType::Untracked, GitStatusChar::Untracked } } } );
}
s.totalInserts += inserts;
s.totalDeletions += deletes;
@@ -332,7 +338,7 @@ Git::Status Git::status( bool recurseSubmodules, const std::string& projectDir )
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(.*)" );
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 );
@@ -342,29 +348,150 @@ Git::Status Git::status( bool recurseSubmodules, const std::string& projectDir )
line.substr( matches[1].start, matches[1].end - matches[1].start ) );
FileSystem::dirAddSlashAtEnd( subModulePath );
} else if ( pattern.matches( line.data(), 0, matches, line.size() ) ) {
auto status = String::trim(
line.substr( matches[1].start, matches[1].end - matches[1].start ) );
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 );
FileStatus rstatus = FileStatus::Unknown;
if ( "??" == status )
rstatus = FileStatus::Untracked;
else if ( "M" == status )
rstatus = FileStatus::Modified;
else if ( "A" == status )
rstatus = FileStatus::Added;
else if ( "D" == status )
rstatus = FileStatus::Deleted;
else if ( "R" == status )
rstatus = FileStatus::Renamed;
else if ( "T" == status )
rstatus = FileStatus::TypeChanged;
else if ( "U" == status )
rstatus = FileStatus::UpdatedUnmerged;
else if ( "m" == status )
rstatus = FileStatus::ModifiedSubmodule;
if ( statusStr.size() < 2 )
continue;
if ( rstatus != FileStatus::Unknown ) {
if ( rstatus == FileStatus::ModifiedSubmodule )
Uint16 status = xy( statusStr[0], statusStr[1] );
GitStatus gitStatus = GitStatus::NotSet;
GitStatusChar gitStatusChar = GitStatusChar::Unknown;
GitStatusType gitStatusType = GitStatusType::Untracked;
switch ( status ) {
case StatusXY::DD: {
gitStatus = GitStatus::Unmerge_BothDeleted;
gitStatusChar = GitStatusChar::Deleted;
gitStatusType = GitStatusType::Unmerged;
break;
}
case StatusXY::AU: {
gitStatus = GitStatus::Unmerge_AddedByUs;
gitStatusChar = GitStatusChar::Added;
gitStatusType = GitStatusType::Unmerged;
break;
}
case StatusXY::UD: {
gitStatus = GitStatus::Unmerge_DeletedByThem;
gitStatusChar = GitStatusChar::Deleted;
gitStatusType = GitStatusType::Unmerged;
break;
}
case StatusXY::UA: {
gitStatus = GitStatus::Unmerge_AddedByThem;
gitStatusChar = GitStatusChar::Added;
gitStatusType = GitStatusType::Unmerged;
break;
}
case StatusXY::DU: {
gitStatus = GitStatus::Unmerge_DeletedByUs;
gitStatusChar = GitStatusChar::Deleted;
gitStatusType = GitStatusType::Unmerged;
break;
}
case StatusXY::AA: {
gitStatus = GitStatus::Unmerge_BothAdded;
gitStatusChar = GitStatusChar::Added;
gitStatusType = GitStatusType::Unmerged;
break;
}
case StatusXY::UU: {
gitStatus = GitStatus::Unmerge_BothModified;
gitStatusChar = GitStatusChar::Modified;
gitStatusType = GitStatusType::Unmerged;
break;
}
case StatusXY::QQ: {
gitStatus = GitStatus::Untracked;
gitStatusChar = GitStatusChar::Untracked;
gitStatusType = GitStatusType::Untracked;
break;
}
case StatusXY::II: {
gitStatus = GitStatus::Ignored;
gitStatusChar = GitStatusChar::Ignored;
gitStatusType = GitStatusType::Ignored;
break;
}
case StatusXY::m: {
gitStatus = GitStatus::WorkingTree_ModifiedSubmodule;
gitStatusChar = GitStatusChar::ModifiedSubmodule;
gitStatusType = GitStatusType::Changed;
break;
}
default:
break;
}
if ( gitStatus == GitStatus::NotSet ) {
char x = statusStr[0];
switch ( x ) {
case 'M': {
gitStatus = GitStatus::Index_Modified;
gitStatusChar = GitStatusChar::Modified;
gitStatusType = GitStatusType::Staged;
break;
}
case 'A': {
gitStatus = GitStatus::Index_Added;
gitStatusChar = GitStatusChar::Added;
gitStatusType = GitStatusType::Staged;
break;
}
case 'D': {
gitStatus = GitStatus::Index_Deleted;
gitStatusChar = GitStatusChar::Deleted;
gitStatusType = GitStatusType::Staged;
break;
}
case 'R': {
gitStatus = GitStatus::Index_Renamed;
gitStatusChar = GitStatusChar::Renamed;
gitStatusType = GitStatusType::Staged;
break;
}
case 'C': {
gitStatus = GitStatus::Index_Copied;
gitStatusChar = GitStatusChar::Copied;
gitStatusType = GitStatusType::Staged;
break;
}
case 'm': {
gitStatus = GitStatus::Index_ModifiedSubmodule;
gitStatusChar = GitStatusChar::ModifiedSubmodule;
gitStatusType = GitStatusType::Staged;
break;
}
}
}
if ( gitStatus == GitStatus::NotSet ) {
char y = statusStr[1];
switch ( y ) {
case 'M': {
gitStatus = GitStatus::WorkingTree_Modified;
gitStatusChar = GitStatusChar::Modified;
gitStatusType = GitStatusType::Changed;
break;
}
case 'D': {
gitStatus = GitStatus::WorkingTree_Deleted;
gitStatusChar = GitStatusChar::Deleted;
gitStatusType = GitStatusType::Changed;
break;
}
case 'A': {
gitStatus = GitStatus::WorkingTree_IntentToAdd;
gitStatusChar = GitStatusChar::Added;
gitStatusType = GitStatusType::Changed;
break;
}
}
}
if ( gitStatus != GitStatus::NotSet ) {
if ( gitStatusChar == GitStatusChar::ModifiedSubmodule )
modifiedSubmodule = true;
else {
auto filePath = subModulePath + file;
@@ -374,16 +501,21 @@ Git::Status Git::status( bool recurseSubmodules, const std::string& projectDir )
bool found = false;
for ( auto& fileIt : repoIt->second ) {
if ( fileIt.file == filePath ) {
fileIt.status = rstatus;
fileIt.statusChar = gitStatusChar;
fileIt.status = gitStatus;
fileIt.statusType = gitStatusType;
found = true;
break;
}
}
if ( !found )
s.files[repo].push_back( { std::move( filePath ), 0, 0, rstatus } );
if ( !found ) {
s.files[repo].push_back( { std::move( filePath ), 0, 0, gitStatus,
gitStatusType, gitStatusChar } );
}
} else {
s.files.insert(
{ repo, { { std::move( filePath ), 0, 0, rstatus } } } );
s.files.insert( { repo,
{ { std::move( filePath ), 0, 0, gitStatus,
gitStatusType, gitStatusChar } } } );
}
}
}
@@ -403,7 +535,7 @@ Git::Status Git::status( bool recurseSubmodules, const std::string& projectDir )
for ( auto& [_, repo] : s.files ) {
for ( auto& val : repo ) {
if ( val.status == FileStatus::Added && val.inserts == 0 ) {
if ( val.statusChar == GitStatusChar::Added && val.inserts == 0 ) {
std::string fileText;
FileSystem::fileGet( ( projectDir.empty() ? mProjectPath : projectDir ) + val.file,
fileText );
+76 -3
View File
@@ -1,3 +1,4 @@
#include <cstdint>
#include <map>
#include <string>
#include <vector>
@@ -23,7 +24,7 @@ class Git {
std::size_t line{ 0 };
};
enum class FileStatus : char {
enum class GitStatusChar : char {
Unknown = ' ',
Modified = 'M',
Added = 'A',
@@ -33,17 +34,89 @@ class Git {
Deleted = 'D',
Untracked = '?',
Ignored = 'I',
Copied = 'C',
ModifiedSubmodule = 'm',
};
enum class GitStatus {
Unmerge_BothDeleted,
Unmerge_AddedByUs,
Unmerge_DeletedByThem,
Unmerge_AddedByThem,
Unmerge_DeletedByUs,
Unmerge_BothAdded,
Unmerge_BothModified,
Index_Modified,
Index_Added,
Index_Deleted,
Index_Renamed,
Index_Copied,
Index_ModifiedSubmodule,
WorkingTree_Modified,
WorkingTree_Deleted,
WorkingTree_IntentToAdd,
WorkingTree_ModifiedSubmodule,
Untracked,
Ignored,
NotSet
};
enum class GitStatusType {
Untracked,
Unmerged,
Changed,
Staged,
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
};
struct DiffFile {
std::string file;
int inserts{ 0 };
int deletes{ 0 };
FileStatus status{ FileStatus::Unknown };
GitStatus status;
GitStatusType statusType;
GitStatusChar statusChar{ GitStatusChar::Unknown };
bool operator==( const DiffFile& other ) const {
return file == other.file && inserts == other.inserts && deletes == other.deletes;
return file == other.file && inserts == other.inserts && deletes == other.deletes &&
status == other.status && statusType == other.statusType &&
statusChar == other.statusChar;
}
};
+1 -1
View File
@@ -235,7 +235,7 @@ class GitStatusModel : public Model {
case Column::Removed:
return Variant( String::format( "-%d ", s.deletes ) );
case Column::State:
return Variant( String::format( "%c", s.status ) );
return Variant( String::format( "%c", s.statusChar ) );
case Column::RelativeDirectory:
return Variant( FileSystem::fileRemoveFileName( s.file ) );
}