AcUtils
A high performance abstraction layer for AccuRev
FileHist.cs

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
user="barnyrd">
<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"
dir="no"/>
</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
user="barnyrd"
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"
elem_type="text"
dir="no"/>
<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"
elem_type="text"
dir="no"/>
</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
user="barnyrd"
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"
elem_type="text"
dir="no"/>
<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"
elem_type="text"
dir="no"
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.

/* Copyright (C) 2016-2018 Verizon. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
// Required references: AcUtils.dll, System.Xml.Linq
using System;
using System.Xml.Linq;
using AcUtils;
namespace FileHist
{
class Program
{
static int Main()
{
string depot = "MARS";
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}"; // reverse start-end times as workaround for AccuRev issue 15780
AcResult result = await AcCommand.runAsync($@"hist -p ""{depot}"" -t ""{time}"" -e {eid} -fevx");
if (result == null || result.RetVal != 0) return false; // operation failed, check log file
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")} " + // transaction ID
$"{{{(string)t.Attribute("type")}}}, " + // transaction type, e.g. keep, move, promote, purge, etc.
$"{t.acxTime("time")}"); // convert Epoch "time" attribute to .NET DateTime object
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()}"); // attributes that exist for promote transactions only
string virtualNamed = t.acxVirtualNamed();
if (!String.IsNullOrEmpty(virtualNamed)) Console.WriteLine($"Virtual: {virtualNamed}"); // a promote or co operation
Console.WriteLine();
foreach (XElement v in t.Elements("version"))
{
string realNamed = v.acxRealNamed();
if (String.IsNullOrEmpty(realNamed)) continue; // null in first (redundant) version element in promote transactions
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"); // convert Epoch "mtime" attribute
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;
}
}
}