// 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. |