FileHead.cpp #1

  • //
  • guest/
  • samwise/
  • p4hl/
  • src/
  • dlls/
  • FileHead.cpp
  • View
  • Commits
  • Open Download .zip Download (7 KB)
// FileHead.cpp: implementation of the FileHead class.
//
//////////////////////////////////////////////////////////////////////

#include "FileHead.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

/* 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)
{
	file = path; //This is the path to the file.
	next = NULL;
	head = NULL;
	tail = NULL;

	parent = newparent; //The FileLogCache that created this FileHead.
	
	ClientLogUser ui;
	ui.working = this; //Tell the ClientLogUser to add revisions to this FileHead.
	ClientApi client;
	Error e;
	StrBuf msg;
	
	/* Initialize the client/server connection.  Any error at this point is the
	 * result of a bad connection between client and server. */
	client.Init(&e);
	if (e.GetSeverity()) {
		e.Fmt(&msg);
		ALERT(at_console, msg.Text());
		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.
	
	client.SetArgv(1, args); //Set the argument list to be the file path.
	client.Run( "filelog", &ui ); //Run "p4 filelog <file>".

	/* 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. */
	
	/* Close the connection and report errors to the console. */
	client.Final (&e);
	if (e.GetSeverity()) {
		e.Fmt(&msg);
		ALERT(at_console, msg.Text());
	}
}

/* The destructor - deletes all of its FileRevs as well as its neighboring FileHeads. */
FileHead::~FileHead()
{
	if (head != NULL) {
		delete head; //deleting the head FileRev deletes them all.
	}
	if (next != NULL) {
		delete next; //pass the deletion message on to the next FileHead.
	}
}

/* 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 newrev, StrBuf newchange, short type)
{
	if (head != NULL) //if we already have one or more FileRevs here
	{
		tail->next = new FileRev(newrev, this, newchange, type); //tack this on the end
		tail = tail->next; //update the tail pointer to reflect the addition
	}
	else //this is the first revision we're adding
	{
		head = new FileRev(newrev, this, newchange, type); //make it the head
		tail = head; //and make it the tail also
	}
}

/* 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)
{
	StrBuf filename; //This is the path to the file we'll be calling scanInto on next.
	StrBuf filerev; //This is the startrev for that file (the rev created by the integ).
	FileRev* foo = head; //foo is the FileRev we're looking at.  Start with head.
	FileHead* tocheck; //tocheck is the FileHead corresponding to file 'filename'.
	while (foo != NULL) //Unless we hit the end of the line...
	{
		if (foo->intocheck) { //Have we already checked this rev?  If so, don't do it again.
			if (foo->rev == startrev) return; //If this is startrev, we're done.
			foo = foo->next; //otherwise, move on to the next FileRev.
			continue; //Next loop.
		}
		/* If we reach this point, this rev hasn't been checked yet. */
		filename = foo->intofiles.SPop();
		filerev = foo->intorevs.SPop();
		/* If foo doesn't have any integ children, these will be empty StrBufs. */
		while (filename.Length()) //If length > zero, it must be a filename.
		{
			/* 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. */
			tocheck = parent->Get(filename, false); //get the FileHead
			tocheck->scanInto(filerev, foo); //scanInto it
			filename = foo->intofiles.SPop(); //next file
			filerev = foo->intorevs.SPop();   //rev of that file
		}
		foo->intocheck = true; //Record the fact that this FileRev has been checked.
		parent->changes->AddChange(foo->change); //add this change to the sorted list
		if (foo->rev == startrev) //Is this the startrev?
		{
			foo->from = caller; //Set its from pointer to the caller FileRev.
			return; //We're done.
		}
		foo = foo->next; //Otherwise, move on to the next FileRev.
	}
}

/* scanFrom returns a FileRev* , which is then used to fill the "from" pointer
 * of its caller.  The endrev argument indicates the end of the revision range -
 * in scanFrom, we start at that rev and then work our way down to the earliest
 * revision. */
FileRev* FileHead::scanFrom(StrBuf endrev)
{
	StrBuf filename;
	StrBuf filerev;
	bool credit = true; //If no endrev is given, scan all revs.
	if (endrev.Length()) credit = false; //default - don't scan till the endrev.
	FileRev* foo = head; //foo is the rev we're currently looking at.
	FileRev* bar = NULL; //bar is what we'll ultimately return.
	FileHead* tocheck; //tocheck is what we'll call scanFrom on next.
	while (foo != NULL) //for each FileRev...
	{
		if (foo->rev == endrev) { //if this is the endrev
			credit = true; //begin doing scanFroms
			bar = foo; //this is the rev to return
		}
		if (credit) //If we've started scanning
		{
			if (foo->fromcheck) //If this rev has already been checked
			{
				foo = foo->next; //Skip over it and continue.
				continue;
			}
			filename = foo->fromfile; //get the fromfile if any
			filerev = foo->fromrev; //get the fromrev if any
			if (filename.Length()) //If it's there and contains something
			{
				tocheck = parent->Get(filename, true); //get the associated FileHead
				foo->from = tocheck->scanFrom(filerev); //call its scanFrom method
			}
			foo->fromcheck = true; //mark this FileRev as having been scanned
			parent->changes->AddChange(foo->change); //add this change to the list
		}
		foo = foo->next; //next FileRev
	}
	return bar; //return the value we stored earlier
}
# Change User Description Committed
#2 937 Sam Stafford Renaming my guest directory to the more conventional
sam_stafford.
#1 936 Sam Stafford Adding P4HL to the public depot.
 See relnotes.txt for
installation instructions; all relevant files are under
p4hl/dist.

Source code is under p4hl/src in the form of a VC++ project.