One of the difficulties working with XML emitted by AccuRev's hist
command via the -fx
option (display results in XML format) is that the result is not always what you'd expect. For example, the data shown here for the EID's version element is correct. (Start/end times reversed as workaround for AccuRev issue 15780.)
accurev hist -p MARS -t "2016/11/01 14:30:00 - 2016/11/01 11:30:00" -fx
<?xml version="1.0" encoding="utf-8"?>
<AcResponse
Command="hist"
TaskId="33057">
<transaction
id="83126"
type="promote"
time="1478023744" Tue Nov-1 14:09:04 2016
<comment>Promote refresh button.</comment>
<version
path="\.\src\foo.java"
eid="594"
virtual="8/1"
real="58/1"
virtualNamedVersion="MARS_DEV2/1"
realNamedVersion="MARS_DEV2_barnyrd/1"
elem_type="text"
</transaction>
...
However, if you add the -e
: ("expanded") option to display more detail for promote
transactions, instead of adding the additional attributes to the version element, AccuRev adds a second version element that is a duplicate (well, almost) of the first, but with the added information. Unfortunately, the second version element is missing the path and eid attributes from the first, so you must reference the first version element to get those values (or add the -v
("verbose") option shown next).
But that's not the only problem; the data is wrong! The virtual and virtualNamedVersion attribute values are the same as the real and realNamedVersion attributes. This falls under AccuRev defect 18636, RPI # 1107275.
accurev hist -p MARS -t "2016/11/01 14:30:00 - 2016/11/01 11:30:00" -fex
<?xml version="1.0" encoding="utf-8"?>
<AcResponse
Command="hist"
TaskId="12964">
<transaction
id="83126"
type="promote"
time="1478023744" Tue Nov-1 14:09:04 2016
next four attributes added by the -e ("expanded") option
streamName="MARS_DEV2"
streamNumber="8"
fromStreamName="MARS_DEV2_barnyrd"
fromStreamNumber="58">
<comment>Promote refresh button.</comment>
<version
path="\.\src\foo.java"
eid="594"
virtual="8/1"
real="58/1"
virtualNamedVersion="MARS_DEV2/1"
realNamedVersion="MARS_DEV2_barnyrd/1"
<comment>Add refresh button.</comment>
<version
missing path and eid attributes; get them
from above
or add the -v ("verbose") option as shown below
virtual="58/1" incorrect value
real="58/1"
virtualNamedVersion="MARS_DEV2_barnyrd/1" incorrect value
realNamedVersion="MARS_DEV2_barnyrd/1"
ancestor="27/2"
ancestorNamedVersion="MARS_MAINT1_barnyrd/2"
</transaction>
...
If you then add the -v
: ("verbose") option to the command, instead of a third version element being added, the second version element is modified to include the requested information. The path and eid attributes are now back, but unfortunately the virtual attribute data is still wrong.
accurev hist -p MARS -t "2016/11/01 14:30:00 - 2016/11/01 11:30:00" -fevx
<?xml version="1.0" encoding="utf-8"?>
<AcResponse
Command="hist"
TaskId="10498">
<transaction
id="83126"
type="promote"
time="1478023744" Tue Nov-1 14:09:04 2016
next four attributes added by the -e ("expanded") option
streamName="MARS_DEV2"
streamNumber="8"
fromStreamName="MARS_DEV2_barnyrd"
fromStreamNumber="58">
<comment>Promote refresh button.</comment>
<version
path="\.\src\foo.java"
eid="594"
virtual="8/1"
real="58/1"
virtualNamedVersion="MARS_DEV2/1"
realNamedVersion="MARS_DEV2_barnyrd/1"
<comment>Add refresh button.</comment>
<version
path and eid attributes are now back
path="\.\src\foo.java"
eid="594"
virtual="58/1" still incorrect
real="58/1"
virtualNamedVersion="MARS_DEV2_barnyrd/1" still incorrect
realNamedVersion="MARS_DEV2_barnyrd/1"
ancestor="27/2"
ancestorNamedVersion="MARS_MAINT1_barnyrd/2"
next three added by the -v (verbose) option
mtime="1477941921" Mon Oct-31 15:25:21 2016
cksum="2657386538"
sz="6863"/>
</transaction>
...
Version comments, if they exist, are comment sibling elements directly above their respective version element, except in the case of promote
transactions where the comment element above the first version element listed in the parent transaction is the comment for the transaction itself. (Sure, feel free to read that again.) There could also be additional version elements listed whose changes were included in the main version during development. They would include the merged_against and mergedAgainstNamedVersion attributes.
So if you want to consume the XML programmatically, what can you do? You can imagine the code to handle all of this would become awfully messy very quickly, right? And just think of the poor maintenance programmer! (They might come looking for you.)
Considering the plethora of hist
command line options available and their differing results, the way in which multiple version elements exist with redundant data, missing attributes and/or incorrect values, different meanings for the same element based on document order, and the potential for differences based on the version of AccuRev in use, solving the problem is best handled in user code. Trying to solve it in the AcUtils library would be too fragile for all of these reasons. Fortunately, after examining the XML generated by the command you want to use, the solution is to use LINQ to XML, some custom LINQ query operators from AcUtils designed to address these (ahem) incongruities, and some conditional statements in your code.
For example, let's say you'd like a report similar to the one given for the history of a file over a specified time period that is displayed when the command result is not in XML format (no -fx
option used). There are aspects to this report you feel could be improved upon and you'd like a different format. Here's a program that will generate such a report.
namespace FileHist
{
class Program
{
static int Main()
{
int eid = 594;
string startTime = "2016/11/01 11:30:00";
string endTime = "2016/11/01 14:30:00";
Task<bool> r = fileHistAsync(depot, eid, startTime, endTime);
bool ret = r.Result;
return (ret) ? 0 : 1;
}
private static async Task<bool> fileHistAsync(
string depot,
int eid,
string startTime,
string endTime)
{
Console.WriteLine($@"Depot: {depot}, EID: {eid} ""{startTime} - {endTime}""{Environment.NewLine}");
string time = $"{endTime} - {startTime}";
AcResult result = await AcCommand.runAsync($@"hist -p ""{depot}"" -t ""{time}"" -e {eid} -fevx");
if (result == null || result.RetVal != 0) return false;
XElement xml = XElement.Parse(result.CmdResult);
XElement e = xml.Element("element");
foreach (XElement t in e.Elements("transaction"))
{
Console.WriteLine($"Transaction: {(int)t.Attribute("id")} " +
$"{{{(string)t.Attribute("type")}}}, " +
$"{t.acxTime("time")}");
string tcomment = t.acxComment();
Console.WriteLine($
"User: {(string)t.Attribute("user")}{(String.IsNullOrEmpty(tcomment) ? String.Empty : ",
" + tcomment)}");
string fromStream = t.acxFromStream();
if (!String.IsNullOrEmpty(fromStream))
Console.WriteLine($"From {fromStream} to {t.acxToStream()}");
string virtualNamed = t.acxVirtualNamed();
if (!String.IsNullOrEmpty(virtualNamed)) Console.WriteLine($"Virtual: {virtualNamed}");
Console.WriteLine();
foreach (XElement v in t.Elements("version"))
{
string realNamed = v.acxRealNamed();
if (String.IsNullOrEmpty(realNamed)) continue;
string vcomment = v.acxComment();
if (!String.IsNullOrEmpty(vcomment)) Console.WriteLine("\t" + vcomment);
string path = (string)v.Attribute("path");
if (!String.IsNullOrEmpty(path)) Console.WriteLine($"\tEID: {eid} {path}");
DateTime? mtime = v.acxTime("mtime");
Console.WriteLine($"\tReal: {realNamed} {((mtime == null) ? String.Empty : "Modified: " + mtime)}");
string ancestorNamed = v.acxAncestorNamed();
if (!String.IsNullOrEmpty(ancestorNamed)) Console.WriteLine($"\tAncestor: {ancestorNamed}");
string mergedAgainstNamed = v.acxMergedAgainstNamed();
if (!String.IsNullOrEmpty(mergedAgainstNamed)) Console.WriteLine($"\tMerged against: {mergedAgainstNamed}");
Console.WriteLine();
}
Console.WriteLine("--------------------------------------------------------");
}
return true;
}
}
}