// Implementation of FileHead. Lots of the nitty-gritty code for // tracing integ records is here. #include <clientapi.h> #include "changesorter.h" #include "filehead.h" #include "clientloguser.h" #include "filelogcache.h" // When a FileHead is constructed, it immediately populates itself with // all the necessary FileRev objects to make it a complete representation // of this file. Integration records are not evaluated at this time. FileHead::FileHead( StrBuf path, FileLogCache* newparent ) : size( 0 ), name( path ), next( NULL ), head( NULL ), tail( NULL ), next_from( NULL ), next_into( NULL ), flc( newparent ) { size = 0; name = path; //This is the path to the file. next = NULL; head = NULL; tail = NULL; next_from = NULL; next_into = NULL; flc = newparent; //The FileLogCache that created this FileHead. ui = new ClientLogUser( this, flc->errors ); if ( flc->cachefile ) { if ( LoadCached( flc->cachefile ) ) return; } if ( flc->errors->IsFatal() ) return; char* args[1]; //An array that will hold the arguments to the p4 command. args[0] = path.Text(); //Path to the file - this will be the arg to p4 filelog. flc->client->SetArgv( 1, args ); flc->client->RunTag( "filelog", ui ); // At this point, the ClientLogUser takes care of creating FileRevs and // arranging them. By the time the filelog command finishes executing, // it's all done. if ( flc->client->Dropped() ) flc->client->Final( flc->errors ); } // The destructor - deletes all FileRevs and neighboring FileHeads. FileHead::~FileHead() { delete ui; if ( head ) delete head; if ( next ) delete next; } bool FileHead::LoadCached( char* filename ) { FILE* file = fopen( filename, "r" ); if ( !file ) return false; bool success = false; char* c = new char[1024]; while ( fgets( c, 1024, file ) ) { if ( !strncmp( c, name.Text(), name.Length() ) ) { success = true; ui->OutputInfo( '0', c ); break; } } if ( !success ) return false; while ( fgets( c, 1024, file ) ) { if ( !strncmp( c, "... ... ", 8 ) ) ui->OutputInfo( '2', c+8 ); else if ( !strncmp( c, "... ", 4 ) ) ui->OutputInfo( '1', c+4 ); else if ( !strncmp( c, "//", 2 ) ) ui->OutputInfo( '0', c ); } return true; } /* addRev is called from within ClientLogUser each time a new revision is given in * the filelog. It adds a revision to the tail of the chain of FileRevs. */ void FileHead::addRev( StrBuf nrev, StrBuf nchange, RevType type, StrBuf da ) { // Error checking - this will reject blatantly invalid results if ( nchange.Length() == 0 || nchange.Atoi() == 0 || nrev.Length() == 0 || nrev.Atoi() == 0 ) return; size++; if ( head ) // already have one or more FileRevs here { tail->next = new FileRev( nrev, this, nchange, type, da ); tail->next->prev = tail; // add appropriate "edit" FileRevArrow tail->AddFromArrow( tail->next, edit ); tail = tail->next; } else // head rev { head = new FileRev( nrev, this, nchange, type, da ); tail = head; } flc->changes->AddChange( nchange, tail ); } // See the header file - scanInto is called when another FileHead has // determined that this one is its descendant. The first argument indicates // at what point this FileHead received content from the donor FileHead, and // the second argument indicates what specific revision that integration was // from. Note that scanInto operates on all the revisions from startrev-head. // Note also that a FileHead's revisions start with the head and work // backwards, so we'll be starting with the head revision and working down // until we hit startrev - when we hit startrev, we point it at the "caller" // FileRev and return. void FileHead::scanInto( StrBuf startrev, FileRev* caller, ArrowType atype ) { bool credit = flc->showall; // Don't start looking yet unless showall. FileRev* fr; //fr is the FileRev we're looking at. FileTextArrow* text; // First, cache all of the descendant branches. for ( fr = tail ; fr ; fr = fr->prev ) // from #1 to #head { if ( fr->rev == startrev ) credit = true; // begin caching if ( !credit || fr->intocheck ) continue; for ( text = fr->intotexts ; text ; text = text->next ) text->fhead = flc->Get( text->file, false, this ); } flc->client->WaitTag(); // Now we scanInto all those precached FileHeads. credit = flc->showall; fr = tail; for ( fr = tail ; fr ; fr = fr->prev ) { if ( fr->rev == startrev ) { credit = true; // begin calling ScanInto fr->AddFromArrow( caller, atype ); // Set a FRA to the caller } if ( !credit || fr->intocheck ) continue; // This rev needs to be checked. fr->intocheck = true; // Don't visit it again. for ( text = fr->intotexts ; text ; text = text->next ) { // Here we find the FileHead representing the file from the // "into" integ record. We then scanInto it, giving the file // that we're looking at right now as the "caller" arg, and // the revision from the integ record as the startrev. if ( text->fhead->size == 0 ) continue; else text->fhead->scanInto( text->rev, fr, text->type ); } } if ( flc->showall ) scanMain(); } // scanFrom returns a FileRev* , which is then used to fill the "from" pointer // of its caller. The endrev argument indicates the end of the rev range - in // scanFrom, we start there and then work our way down to the earliest rev. FileRev* FileHead::scanFrom( StrBuf endrev ) { FileTextArrow* text; FileRev* fr; for ( fr = tail ; fr ; fr = fr->prev ) { if ( fr->fromcheck && fr->rev == endrev && !flc->showall ) break; else if ( fr->fromcheck ) continue; for ( text = fr->fromtexts ; text ; text = text->next ) text->fhead = flc->Get( text->file, true, this ); if ( !fr->fromtexts ) fr->fromcheck = true; if ( fr->rev == endrev && !flc->showall ) break; // done here } flc->client->WaitTag(); // Now the actual scans. FileRev* val = NULL; for ( fr = tail ; fr ; fr = fr->prev ) { if ( fr->fromcheck && fr->rev == endrev ) { if ( !flc->showall ) return fr; else val = fr; } else if ( fr->fromcheck ) continue; fr->fromcheck = true; // soon to be done with this rev for ( text = fr->fromtexts ; text ; text = text->next ) { if ( !text->fhead->size ) continue; // empty FH, error fr->AddFromArrow ( text->fhead->scanFrom( text->rev ), text->type ); } if ( fr->rev == endrev ) // done with this file if !showall { if ( !flc->showall ) return fr; else val = fr; } } if ( flc->showall ) scanMain(); return val; // if showall } void FileHead::scanMain() { FileTextArrow* text; FileRev* fr; // In order to help make a nicely-arranged branch graph, we're going to // precache all of this file's immediate relatives before tracing them // to find the "extended family". for ( fr = tail ; fr ; fr = fr->prev ) { if ( !fr->fromcheck ) for ( text = fr->fromtexts ; text ; text = text->next ) text->fhead = flc->Get( text->file, true, this ); if ( !fr->intocheck ) for ( text = fr->intotexts ; text ; text = text->next ) text->fhead = flc->Get( text->file, false, this ); } flc->client->WaitTag(); // finish creating all revisions // Now we finish it up by actually going through and scanning the immediate // relatives. for ( fr = tail ; fr ; fr = fr->prev ) { if ( !fr->fromcheck ) { fr->fromcheck = true; for ( text = fr->fromtexts ; text ; text = text->next ) { if (text->fhead->size == 0) continue; fr->AddFromArrow ( text->fhead->scanFrom( text->rev ), text->type ); } } if ( !fr->intocheck ) { fr->intocheck = true; text = fr->intotexts; for ( text = fr->intotexts ; text ; text = text->next ) { // Here we find the FileHead representing the file from the "into" // integ record. We then scanInto it, giving the filerev that we're // looking at right now as the "caller" arg, and the revision from // the integ record as the startrev. if (text->fhead->size == 0) continue; text->fhead->scanInto( text->rev, fr, text->type ); } } } }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#4 | 2946 | Sam Stafford |
Pull in read-filelogs-from-file capabilities. No functional change yet. |
||
#3 | 2945 | Sam Stafford | These changes are all already reflected in P4QTree. | ||
#2 | 2398 | Sam Stafford |
Support for PB's "show all" feature. Still needs the UI bits to turn it on. Infrastructure change. |
||
#1 | 2377 | Sam Stafford | P4QTree. |