DeepAnnotate.cpp #1

  • //
  • guest/
  • sam_stafford/
  • deepannotate/
  • DeepAnnotate.cpp
  • View
  • Commits
  • Open Download .zip Download (5 KB)
#include "DeepAnnotate.h"

#include <clientapi.h>
#include <options.h>
#include <strtable.h>

#include "DAFile.h"
#include "DAFileDict.h"
#include "DAInteg.h"
#include "DALine.h"
#include "DARev.h"
#include "DAUserAnnotate.h"
#include "DAUserFilelog.h"
#include "DATextTable.h"
#include "DAZipper.h"

DeepAnnotate::DeepAnnotate( const char* cmd )
{
	cmdName = new StrBuf( cmd );
	fileDict = new DAFileDict;
	textTable = new DATextTable;
	client = 0x0;
	startFile = 0x0;
	tagged = allrevs = quiet = false;
}

DeepAnnotate::~DeepAnnotate(void)
{
	delete cmdName;
	delete fileDict;
	delete textTable;
}

void DeepAnnotate::Run( ClientApi* c, ClientUser* ui, int argc, char** argv )
{
	client = c;

	Error e;
	StrBuf msg( "Usage: " );
	msg.Append( cmdName );
	msg.Append( " [-acq] file" );
	ErrorId usage = { E_FAILED, msg.Text() };

	Options opts;
	opts.Parse( argc, argv, "acIqd:", OPT_ONE, usage, &e );
	if ( e.Test() )
	{
		ui->Message( &e );
		return;
	}

	opts['I'];
	opts['c'];
	opts['d'];
	if ( opts['q'] ) quiet = true;
	if ( opts['a'] ) allrevs = true;

	//Build basic data structures.
	startFile = LoadFile( StrRef(argv[0]), ui );
	if ( !startFile ) return; //error
	LoadAncestors( startFile );

	//Zip related revisions together.
	for ( int r = 1 ; r <= startFile->head() ; r++ )
	{
		Zip( startFile, r );
	}

	SendOutput( ui );
}

void DeepAnnotate::SendOutput( ClientUser* ui )
{
	DALine* oldLine = 0x0;
	DALine* newLine = startFile->zeroLine();
	DARev *uRev, *lRev;
	if ( allrevs ) newLine = newLine->nextLine();
	else newLine = newLine->nextAsOf( startFile->head() );
	StrPtrDict varList;
	StrBuf outputText;
	while ( newLine->text() )
	{
		oldLine = newLine;
		while ( oldLine->ancestor() )
		{
			oldLine = oldLine->ancestor();
		}
		uRev = newLine->getFile()->getLastRev( newLine->upperRev() );
		lRev = oldLine->getFile()->getNextRev( oldLine->lowerRev() );
		if ( tagged && lRev )
		{
			if ( uRev ) varList.SetVar( "upper", StrNum( uRev->getChange() ) );
			varList.SetVar( "lower", StrNum( lRev->getChange() ) );
			varList.SetVar( "data", textTable->getText( oldLine->text() ) );
			ui->OutputStat( &varList );
		}
		else if ( lRev )
		{
			outputText.Set( StrNum( lRev->getChange() ) );
			if ( allrevs && uRev )
			{
				outputText.Append( "-" );
				outputText.Append( &StrNum( uRev->getChange() ) );
			}

			outputText.Append( ": " );
			outputText.Append( &textTable->getText( newLine->text() ) );
			ui->OutputText( outputText.Text(), outputText.Length() );
		}
		if ( allrevs ) newLine = newLine->nextLine();
		else newLine = newLine->nextAsOf( startFile->head() );
	}
}

DAFile* DeepAnnotate::LoadFile( const StrPtr& path, ClientUser* ui )
{
	if ( !path.Length() ) return 0x0;
	DAFile* f;
	if ( f = fileDict->Get( path ) ) return f;

	f = new DAFile( textTable );

	DAUserFilelog ui1( f, &tagged );
	char* argv[2];
	argv[0] = path.Text();
	client->SetArgv( 1, argv );
	client->Run( P4Tag::u_filelog, &ui1 );

	if ( !fileDict->Put( f->getPath(), f ) )
	{
		delete f;
		return 0x0;
	}

	if ( tagged && !startFile ) client->SetVar( P4Tag::v_tag, "yes" );

	DAUserAnnotate ui2( f, ui, quiet );
	argv[0] = "-a";
	argv[1] = path.Text();
	client->SetArgv( 2, argv );
	client->Run( "annotate", &ui2 );	

	return f;
}

void DeepAnnotate::LoadAncestors( DAFile* file )
{
	// Build a list of ancestor files that have not
	// yet been loaded.
	StrPtrDict ancestors;
	DARev* rev;
	DAInteg* integ;
	StrRef foo( "foo" );
	for ( int r = 1 ; r <= file->head() ; r++ )
	{
		rev = file->getRev( r );
		if ( !rev ) continue;

		for ( int i = 0 ; i < rev->integCount(); i++ )
		{
			integ = rev->getInteg( i );
			if ( !integ ) continue;
			if ( fileDict->Get( integ->getFilePath() ) ) continue;
			if ( ancestors.GetVar( integ->getFilePath() ) ) continue;

			ancestors.VSetVar( integ->getFilePath(), foo );
		}
	}

	// Load each new file.
	StrRef anc, val;
	for ( int f = 0 ; ancestors.GetVar( f, anc, val ) ; f++ )
	{
		LoadFile( anc );
	}

	// Recurse on each new file.
	DAFile* ancfile;
	for ( int f = 0 ; ancestors.GetVar( f, anc, val ) ; f++ )
	{
		if ( !( ancfile = fileDict->Get( anc ) ) ) continue;
		LoadAncestors( ancfile );
	}

	// Set pointers on all integ records.
	for ( int r = 1 ; r <= file->head() ; r++ )
	{
		rev = file->getRev( r );
		if ( !rev ) continue;

		for ( int i = 0 ; i < rev->integCount(); i++ )
		{
			integ = rev->getInteg( i );
			if ( !integ ) continue;

			integ->FindPointer( fileDict );
		}
	}
}

void DeepAnnotate::Zip( DAFile* file, int rev )
{
	if ( !file->getRev( rev ) ) return;

	DAZipper zipper( file, rev );
	zipper.Zip();

	//Recurse on all newly discovered ancestors.
	for (	DALine* l = file->zeroLine()->nextAsOf( rev ) ;
			l->text();
			l = l->nextAsOf( rev )	)
	{
		if ( l->ancestor() && !l->ancestor()->zipped() )
			Zip( l->ancestor()->getFile(), l->ancestor()->lowerRev() );
	}
}
# Change User Description Committed
#3 6299 Sam Stafford Ditch the attempts at only processing lines that have already been
connected to the starting file.  It seemed to be causing more
backtracking than anything else when the history was complex, and
iterating through all files/revisions works just as well and isn't
nearly as prone to duplicating work.
#2 6298 Sam Stafford Add some new sanity checks.
 One prevents an infaloop observed while
testing against a file with especially twisted history; the other may
help with future tweaks to the zipper algorithm.
#1 6297 Sam Stafford Work so far on "deep annotate".
 Been getting a lot of questions on
this lately from other people working on the same thing; might as well
pool efforts.