//
// Copyright 2014 Perforce Software Inc.
//
using log4net;
using Perforce.Model;
using Perforce.P4;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.Caching;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace Perforce.Helper {
public class PerforceHelper : IDisposable {
enum PerforceErrors {
NoFilesToSubmit = 806427694,
NoFilesToSubmitWithError = 822418474
}
private string _serverUri;
private string _username;
private string _password;
private string _ticket;
private Client _client;
private static readonly ILog _log = LogManager.GetLogger(typeof(PerforceHelper));
#region LOGIN AND CONNECTION TESTING
///
/// Creates a connection to the Perforce server
///
/// true if connected, false otherwise
public bool Connect() {
var connected = false;
var con = GetConnection();
if (con.Status == ConnectionStatus.Disconnected) {
try {
con.Connect(null);
} catch (P4Exception pe) {
throw new P4HelperException(pe.Message);
}
}
if (con.Status == ConnectionStatus.Connected) {
connected = true;
}
return connected;
}
///
/// Disconnects from the Perforce server
///
public void Disconnect() {
var con = GetConnection();
if (con != null && con.Status == ConnectionStatus.Connected) {
con.Disconnect();
}
}
///
/// Tests to see if the connection has a Connected status
///
/// true if connected, false otherwise
public bool IsConnected() {
return GetRepository().Connection.Status == ConnectionStatus.Connected;
}
///
/// Logs into the Perforce server using the specified password
///
/// Thrown if a P4Exception is caught
public Tuple Login(string password) {
var result = new Tuple(false, string.Empty);
var con = GetConnection(useCache: false, testConnection: false);
if (Connect()) {
try {
var options = new LoginCmdOptions(LoginCmdFlags.AllHosts, string.Empty);
var cred = con.Login(password, options);
if (cred != null) {
if (string.IsNullOrEmpty(cred.Ticket)) {
options = new LoginCmdOptions(LoginCmdFlags.AllHosts | LoginCmdFlags.DisplayTicket, string.Empty);
cred = con.Login(password, options);
}
result = new Tuple(true, cred.Ticket);
Ticket = cred.Ticket;
}
} catch (P4Exception pe) {
throw new P4HelperException(pe.Message);
}
}
return result;
}
///
/// Checks to see if the current session is logged in
///
/// true if logged in, false otherwise
public bool IsLoggedIn() {
var result = false;
var con = GetConnection(useCache: false, testConnection: false);
if (con != null) {
try {
string[] args = { "-s" };
P4Command cmd = new P4Command(con, "login", false, args);
var r = cmd.Run();
result = true;
} catch (P4Exception) {
// do nothing... an exception is thrown if login -s fails, indicating that
// the user is not logged in
}
}
return result;
}
///
/// Logs out the current session
///
/// returns the result of the logout operation
public bool Logout() {
var result = false;
var con = GetConnection(useCache: false, testConnection: false);
if (con != null) {
var options = new LogoutCmdOptions(LogoutCmdFlags.AllHosts, string.Empty);
result = con.Logout(options);
}
return result;
}
#endregion
#region SERVER OPERATIONS
public long GetServerTime() {
return Utility.GetEpochTime(GetServer().Metadata.Date.ToUniversalTime());
}
public string GetKey(string name) {
string value = null;
if (string.IsNullOrEmpty(name)) return null;
var args = new List();
args.Add(name);
var cmd = new P4Command(GetRepository(), "key", true, args.ToArray());
var results = cmd.Run();
if (results.TaggedOutput != null) {
foreach (TaggedObject obj in results.TaggedOutput) {
if (obj.ContainsKey("value")) {
var x = obj["value"].ToString();
if (!x.Equals("0")) {
value = x;
}
}
}
}
return value;
}
#endregion
#region USER OPERATIONS
public User GetUserInfo(string userid) {
if (string.IsNullOrEmpty(userid)) return null;
var repo = GetRepository();
return repo.GetUser(userid);
}
#endregion
#region LISTING OPERATIONS
public bool IsDirectory(string path) {
if (path.Equals("//")) return true;
if (path.EndsWith("/")) return true;
if (path.EndsWith("/...")) return true;
var repo = GetRepository();
string[] p = { path };
var options = new GetDepotDirsCmdOptions(GetDepotDirsCmdFlags.None, null);
var results = repo.GetDepotDirs(options, p);
if (results.Count == 1) {
return true;
} else {
var clientPath = PredictClientPath(path);
var exists = Directory.Exists(clientPath);
return exists;
}
}
public bool IsFile(string path) {
var repo = GetRepository();
string[] p = { path };
var specs = GetFileSpecs(p);
var options = new GetDepotFilesCmdOptions(GetDepotFilesCmdFlags.None, 0);
var results = repo.GetDepotFiles(specs, options);
return results.Count == 1;
}
///
/// Gets a list of the depots on the Perforce server
///
/// optional boolean to limit depots to only those of type 'local'
/// A list of Depot specifications
public IList ListDepots(bool localOnly=false) {
IList depots = null;
var repo = GetRepository();
if (repo != null) {
var allDepots = repo.GetDepots();
if (localOnly) {
depots = allDepots.Where(d => d.Type == DepotType.Local).ToList();
} else {
depots = allDepots;
}
}
return depots;
}
///
/// Gets a list of directories given the specified depot path
///
/// The depot path to use as the base
/// A list of string objects
public IList ListDirectories(string path) {
var repo = GetRepository();
if (!path.EndsWith("/")) {
path += "/";
}
string[] p = { path + "*" };
var options = new GetDepotDirsCmdOptions(GetDepotDirsCmdFlags.None, null);
return repo.GetDepotDirs(options, p);
}
///
/// Gets a list of the files at the specified depot path
///
/// The depot path to use as the base
/// A list of FileMetaData (fstat) objects
public IList ListFiles(string depotPath, bool showDeleted=false, long sinceTime = 0) {
var repo = GetRepository();
var filespec = new List();
filespec.Add(new FileSpec(new DepotPath(depotPath), Revision.Head));
string filter = string.Empty;
if (!showDeleted) {
filter = "^headAction=delete ^headAction=move/delete";
}
if (sinceTime > 0) {
if (filter.Length > 0) {
filter += " & ";
}
filter += string.Format("headTime > {0}", sinceTime);
}
var options = new GetFileMetaDataCmdOptions(GetFileMetadataCmdFlags.Attributes|GetFileMetadataCmdFlags.FileSize|GetFileMetadataCmdFlags.HexAttributes, filter, null, -1, null, null, "tags");
return repo.GetFileMetaData(filespec, options);
}
#endregion
#region CLIENT OPERATIONS
///
/// Gets a list of the clients owned by the current user
///
/// A list of Client objects
public IList ListClients(bool validLocalOnly=false) {
var repo = GetRepository();
var options = new ClientsCmdOptions(ClientsCmdFlags.None, _username, null, -1, null);
var clients = repo.GetClients(options);
if (clients != null && validLocalOnly) {
var subset = new List();
foreach (var c in clients) {
var pathRoot = Path.GetPathRoot(c.Root);
if (!pathRoot.StartsWith("\\") && Directory.Exists(pathRoot)) {
subset.Add(c);
}
}
clients = subset;
}
return clients;
}
///
/// Creates a basic client
///
/// The name of the client
/// The root directory for the client
/// A description for the client
/// The client created
public Client CreateClient(string name, string root, string description) {
Client client = null;
if (!ClientExists(name)) {
client = new Client();
client.Name = name;
client.OwnerName = _username;
client.Options = ClientOption.RmDir|ClientOption.Clobber;
client.LineEnd = LineEnd.Local;
client.Description = description;
client.Root = root;
client.SubmitOptions = new ClientSubmitOptions(false, SubmitType.RevertUnchanged);
client.ViewMap = new ViewMap();
} else {
client = GetRepository().GetClient(name);
client.Root = root;
client.Description = description;
}
SaveClient(client);
return client;
}
///
/// Creates a client based on the client specification
///
/// The client specification to create
/// The client created
public Client SaveClient(Client client) {
var result = GetRepository().CreateClient(client);
CurrentClient = client;
return CurrentClient;
}
public Client GetClient(string name) {
return GetRepository().GetClient(name);
}
public Client UpdateClient(Client client) {
GetRepository().UpdateClient(client);
CurrentClient = client;
return CurrentClient;
}
///
/// Checks to see if a particular client already exists on the Perforce server
///
/// The name of the client specification to look for
/// true if the client is found, false otherwise
public bool ClientExists(string name) {
bool found = false;
var repo = GetRepository();
var options = new ClientsCmdOptions(ClientsCmdFlags.IgnoreCase, null, name, -1, null);
var matching = repo.GetClients(options);
if (matching != null && matching.Count > 0) {
found = true;
}
return found;
}
public Client IncludeInClient(string depotPath) {
Client c = GetClient(_client.Name);
MapEntry line = CreateMapEntry(depotPath, _client.Name, MapType.Include);
if (c.ViewMap == null) {
c.ViewMap = new ViewMap();
}
if (!c.ViewMap.Contains(line)) {
// check to see if
c.ViewMap.Add(line);
}
return UpdateClient(c);
}
public Client RemoveFromClient(string depotPath) {
Client updated = null;
Client c = GetClient(_client.Name);
var entry = CreateMapEntry(depotPath, _client.Name, MapType.Include);
var changed = false;
if (c.ViewMap != null && c.ViewMap.Count > 0) {
if (c.ViewMap.Contains(entry)) {
c.ViewMap.Remove(entry);
changed = true;
} else {
// need to iterate over the viewmap to see if this is a subdirectory
// of an existing item
var found = false;
foreach (var mapping in c.ViewMap) {
var left = mapping.Left.Path;
if (left.EndsWith("/...")) {
if (depotPath.StartsWith(left.TrimEnd('.'))) {
found = true;
break;
}
}
}
if (found) {
var excludeEntry = CreateMapEntry(depotPath, _client.Name, MapType.Exclude);
c.ViewMap.Add(excludeEntry);
changed = true;
}
}
}
if (changed) {
updated = UpdateClient(c);
}
return updated;
}
public MapEntry CreateMapEntry(string depotPath, string clientName, MapType type) {
var left = new DepotPath(depotPath);
var clientPath = string.Format("//{0}/{1}", clientName, depotPath.Substring(2));
var right = new ClientPath(clientPath);
return new MapEntry(type, left, right);
}
///
/// Removes the client specification from the server
///
/// The name of the client to delete
public void DeleteClient(string name) {
var repo = GetRepository();
var client = repo.GetClient(name);
if (client != null) {
var options = new DeleteFilesCmdOptions(DeleteFilesCmdFlags.None, -1);
repo.DeleteClient(client, options);
}
}
///
/// CurrentClient property
///
public Client CurrentClient {
get { return _client; }
set {
_client = value;
SetClient(_client.Name) ;
}
}
public void SetClient(string name) {
GetConnection().SetClient(name);
_client = GetClient(name);
var needsUpdate = false;
if (!_client.Options.HasFlag(ClientOption.RmDir)) {
_client.Options = _client.Options | ClientOption.RmDir;
needsUpdate = true;
}
if (!_client.Options.HasFlag(ClientOption.Clobber)) {
_client.Options = _client.Options | ClientOption.Clobber;
needsUpdate = true;
}
if(needsUpdate) {
GetRepository().UpdateClient(_client);
GetConnection().SetClient(name);
_client = GetClient(name);
}
}
#endregion
#region CHANGELIST OPERATIONS
///
/// Creates a new changelist
///
/// The description for the changelist
/// The changelist created
public Changelist CreateChangelist(string description) {
var change = new Changelist();
change.OwnerName = _username;
change.ClientId = CurrentClient.Name;
change.Description = description;
var repo = GetRepository();
change = repo.CreateChangelist(change);
return change;
}
///
/// Retrieves the numbered changelist
///
/// The changelist Id number to retrieve
/// The changelist specified by the Id number
public Changelist GetChangelist(int id, bool includeShelvedFiles = false) {
var repo = GetRepository();
Options opts = null;
if (includeShelvedFiles) {
var flags = DescribeChangelistCmdFlags.Shelved;
opts = new DescribeCmdOptions(flags, 0, 0);
} else {
var flags = ChangeCmdFlags.None;
opts = new ChangeCmdOptions(flags);
}
return repo.GetChangelist(id, opts);
}
public IList GetAllPendingChangelists() {
var repo = GetRepository();
var options = new ChangesCmdOptions(ChangesCmdFlags.None, CurrentClient.Name, 0, ChangeListStatus.Pending, Username);
return repo.GetChangelists(options, null);
}
public Changelist GetCurrentPendingChangelist(bool shelved=false) {
Changelist current = null;
var repo = GetRepository();
var options = new ChangesCmdOptions(ChangesCmdFlags.None, CurrentClient.Name, 1, ChangeListStatus.Pending, Username);
var changes = repo.GetChangelists(options, null);
if (changes != null && changes.Count == 1) {
//current = changes[0];
var id = changes[0].Id;
Options opts = null;
if (shelved) {
var flags = DescribeChangelistCmdFlags.Shelved;
opts = new DescribeCmdOptions(flags, 0, 0);
} else {
var flags = ChangeCmdFlags.None;
opts = new ChangeCmdOptions(flags);
}
current = repo.GetChangelist(id, opts);
}
return current;
}
public void DeletePendingChangeList() {
var change = GetCurrentPendingChangelist();
if (change != null) {
var repo = GetRepository();
var options = new Options();
repo.DeleteChangelist(change, options);
}
}
// Cleans the current changelist, looking for any files that have been deleted locally since
// the changelist was created. The basic idea of this method is to reconcile deletions on local
// disk with the state of the changelist -- something that p4 reconcile doesn't find and
// which causes issues when trying to submit a changelist
public void CleanChangelist() {
var change = GetCurrentPendingChangelist();
// only look at things if the changelist exists and there are files in it
if (change != null && change.Files != null && change.Files.Count > 0) {
var helper = Utility.GetPerforceHelper();
var filesToRevert = new List();
var filesToRemove = new List();
foreach (var f in change.Files) {
if (f.Action == FileAction.Add) {
// if the added file no longer exists, then we just need to revert
var md = helper.GetFileMetaData(f.DepotPath.Path);
if (!System.IO.File.Exists(md.LocalPath.Path)) {
filesToRevert.Add(f.DepotPath.Path);
}
} else if (f.Action == FileAction.MoveAdd) {
// if the renamed file does not exist, we will revert and then delete
// the original
var md = helper.GetFileMetaData(f.DepotPath.Path);
if (!System.IO.File.Exists(md.LocalPath.Path)) {
filesToRevert.Add(f.DepotPath.Path);
filesToRemove.Add(md.MovedFile.Path);
}
} else if (f.Action == FileAction.Edit) {
// we were editing the file, and now it has been deleted so we need to
// mark it for delete
var md = helper.GetFileMetaData(f.DepotPath.Path);
if (!System.IO.File.Exists(md.LocalPath.Path)) {
filesToRevert.Add(f.DepotPath.Path);
filesToRemove.Add(f.DepotPath.Path);
}
}
}
// process any files that need to be reverted first
if (filesToRevert.Count > 0) {
var list = filesToRevert.ToArray();
helper.RevertFiles(serverOnly: false, paths: list);
}
// we only need to save the changelist if we actually cleaned stuff up
if (filesToRemove.Count > 0) {
var list = filesToRemove.ToArray();
helper.DeleteFiles(serverOnly: false, paths: list);
}
}
}
public IList GatherOpenFilesInCurrentChangelist() {
IList reopenedFiles = null;
var repo = GetRepository();
var currentChange = GetOrCreatePendingChangelist();
var openedSpecs = new List();
openedSpecs.Add(GetFileSpec("//..."));
var openedOptions = new GetOpenedFilesOptions(GetOpenedFilesCmdFlags.None, null, CurrentClient.Name, Username, 0);
var list = GetRepository().GetOpenedFiles(openedSpecs, openedOptions);
var reopenSpecs = new List();
foreach (var i in list) {
if (i.ChangeId != currentChange.Id) {
reopenSpecs.Add(new FileSpec(i.DepotPath));
}
}
if (reopenSpecs.Count > 0) {
var options = new ReopenCmdOptions(currentChange.Id, null);
reopenedFiles = repo.Connection.Client.ReopenFiles(reopenSpecs, options);
}
return reopenedFiles;
}
public IList MoveFilesToNewChangelist(IList files, int changeId=0) {
var repo = GetRepository();
if (!(changeId > 0)) {
var change = CreateChangelist("Perforce-created changelist");
changeId = change.Id;
}
var options = new ReopenCmdOptions(changeId, null);
var fileSpecs = new List();
foreach (var f in files) {
if (f.StartsWith("//")) {
fileSpecs.Add(new FileSpec(new DepotPath(f)));
} else {
fileSpecs.Add(new FileSpec(new ClientPath(f)));
}
}
return repo.Connection.Client.ReopenFiles(fileSpecs, options);
}
///
/// Deletes the changelist
///
/// The changelist Id number to delete
public void DeleteChangelist(int id) {
var change = GetChangelist(id);
if (change != null && change.Pending) {
DeleteChangelist(change);
}
}
///
/// Deletes the changelist
///
/// The changelist object to delete
public void DeleteChangelist(Changelist change) {
var repo = GetRepository();
var options = new ChangeCmdOptions(ChangeCmdFlags.Delete);
repo.DeleteChangelist(change, options);
}
public IList ListChanges(string depotPath) {
var repo = GetRepository();
if (IsDirectory(depotPath) && !depotPath.EndsWith("/...")) {
depotPath = depotPath + "/...";
}
var spec = GetFileSpec(depotPath);
var options = new ChangesCmdOptions(ChangesCmdFlags.FullDescription, null, 50, ChangeListStatus.Submitted, null);
return repo.GetChangelists(options, spec);
}
public ResultsWrapper SubmitSingleFile(string depotPath, string description) {
var change = CreateChangelist(description);
var reopenOptions = new ReopenCmdOptions(change.Id, null);
var spec = GetFileSpec(depotPath);
GetRepository().Connection.Client.ReopenFiles(reopenOptions, spec);
return SubmitChangelist(change.Id, description, revertUnchanged: true);
}
public ResultsWrapper SubmitChangelist(int id, string description=null, bool revertUnchanged=true) {
var change = GetChangelist(id);
if (change != null && change.Pending) {
if (description != null) {
change.Description = description;
}
return SubmitChangelist(change, revertUnchanged);
} else {
return null;
}
}
///
///
///
///
///
///
public ResultsWrapper SubmitChangelist(Changelist change=null, bool revertUnchanged=true) {
var wrapper = new ResultsWrapper();
// get the current pending changelist if the changelist is not specified
if (change == null) {
change = GetCurrentPendingChangelist();
if (change == null) {
return null;
}
} else {
GetRepository().UpdateChangelist(change);
}
var clientSubmitOptions = new ClientSubmitOptions();
clientSubmitOptions.Reopen = false;
if (revertUnchanged) {
clientSubmitOptions.SubmitType = SubmitType.RevertUnchanged;
} else {
clientSubmitOptions.SubmitType = SubmitType.SubmitUnchanged;
}
var options = new SubmitCmdOptions(SubmitFilesCmdFlags.None, change.Id, null, null, clientSubmitOptions);
try {
wrapper.Results = GetConnection().Client.SubmitFiles(options, null);
wrapper.HasError = false;
wrapper.Message = "Submit successful";
} catch (P4Exception p4e) {
wrapper.HasError = true;
switch (p4e.ErrorCode) {
case (int)PerforceErrors.NoFilesToSubmit:
wrapper.HasError = false;
wrapper.Message = "There were no changed files to submit";
break;
case (int)PerforceErrors.NoFilesToSubmitWithError:
if (p4e.Details.Count > 0) {
var errorText = new StringBuilder();
foreach (var d in p4e.Details) {
errorText.AppendLine(d.Message);
}
wrapper.Message = errorText.ToString();
}
break;
default:
if (p4e.Details != null && p4e.Details.Count > 0) {
var errorText = new StringBuilder();
foreach (var d in p4e.Details) {
errorText.AppendLine(d.Message);
}
wrapper.Message = errorText.ToString();
} else if(p4e.Message != null) {
wrapper.Message = p4e.Message;
}
break;
}
}
return wrapper;
}
///
///
///
///
///
public IList GetChangelistFiles(Changelist change = null) {
// get the current pending changelist if the changelist is not specified
if (change == null) {
change = GetCurrentPendingChangelist();
}
if (change == null) {
return null;
}
return change.Files;
}
public IList GetShelvedChangelistFiles(Changelist change = null) {
if (change == null) {
change = GetCurrentPendingChangelist(shelved: true);
}
if (change == null) {
return null;
}
return change.ShelvedFiles;
}
///
///
///
///
public IList RevertChangelist(Changelist change=null) {
// get the current pending changelist if the changelist is not specified
if (change == null) {
change = GetCurrentPendingChangelist();
}
if (change == null) {
return null;
}
var fileSpec = new FileSpec(new DepotPath("//..."));
var options = new RevertCmdOptions(RevertFilesCmdFlags.None, change.Id);
return GetRepository().Connection.Client.RevertFiles(options, fileSpec);
}
public Changelist GetOrCreatePendingChangelist() {
var change = GetCurrentPendingChangelist();
if (change == null) {
change = CreateChangelist(Constants.GENERATED_CHANGELIST_DESCRIPTION);
}
return change;
}
#endregion
#region FILE OPERATIONS
public bool PathExists(string depotPath) {
var exists = false;
// first check to see if the path is an existing file
var specs = new List();
specs.Add(GetFileSpec(depotPath));
var fileOptions = new GetDepotFilesCmdOptions(GetDepotFilesCmdFlags.NotDeleted, 0);
try {
var fileResults = GetRepository().GetDepotFiles(specs, fileOptions);
if (fileResults != null && fileResults.Count == 1) {
exists = true;
}
} catch (P4Exception p4e) {
// expected exception if the file does not exist
}
// if the file does not exist, check to see if it is a directory
if (!exists) {
var dirOptions = new GetDepotDirsCmdOptions(GetDepotDirsCmdFlags.None, null);
try {
var dirList = new List();
dirList.Add(depotPath);
var dirResults = GetRepository().GetDepotDirs(dirList, dirOptions);
if (dirResults != null && dirResults.Count == 1) {
exists = true;
}
} catch (P4Exception p4e) {
// expected exception if dir does not exist
}
}
return exists;
}
public bool IsDirectoryMapped(string depotDir) {
// normalize directory
depotDir = depotDir.TrimEnd('/');
var list = new List();
list.Add(depotDir);
var response = GetClientMappings(list, true);
return response.Keys.Contains(depotDir);
}
public bool IsPathInClientView(string depotPath) {
var inView = false;
if (depotPath != null && GetConnection().Client != null && GetConnection().Client.ViewMap != null) {
var map = GetConnection().Client.ViewMap;
foreach (var e in map) {
if (e.Left.Path.StartsWith(depotPath)) {
inView = true;
break;
}
}
}
return inView;
}
//** TODO: need to figure out why this is being called with invalid paths **//
public Dictionary GetClientMappings(IList paths, bool pathsAreDirectories=false) {
var mappings = new Dictionary(StringComparer.CurrentCultureIgnoreCase);
if (paths != null && paths.Count > 0) {
var fileArr = new List();
foreach (var path in paths) {
var p = path;
if (pathsAreDirectories) {
p = string.Format("{0}/%", p);
}
fileArr.Add(p);
}
try {
var cmdResults = WhereResults(fileArr.ToArray());
if (cmdResults.TaggedOutput != null) {
foreach (TaggedObject obj in cmdResults.TaggedOutput) {
if (!obj.ContainsKey("unmap")) {
if (obj.ContainsKey("depotFile") && obj.ContainsKey("path")) {
var left = Utility.NormalizeDepotPath(obj["depotFile"].ToString());
var right = Utility.NormalizeClientPath(obj["path"].ToString());
mappings.Add(left, right);
}
}
}
}
} catch (Exception ex) {
// caught exception, probably because client mappings do not exist.
}
}
return mappings;
}
public P4CommandResult WhereResults(params string[] paths) {
if (paths == null) return null;
var args = new List();
foreach (var p in paths) {
args.Add(p);
}
var cmd = new P4Command(GetRepository(), "where", true, args.ToArray());
return cmd.Run();
}
public Dictionary GetDepotMappings(IList paths, bool pathsAreDirectories = false) {
var mappings = new Dictionary(StringComparer.CurrentCultureIgnoreCase);
if (paths.Count > 0) {
var fromFiles = new List();
foreach (var path in paths) {
var p = path;
if (pathsAreDirectories) {
p = string.Format("{0}/%", p);
}
fromFiles.Add(new FileSpec(new ClientPath(p), null));
}
IList results = null;
try {
results = GetConnection().Client.GetClientFileMappings(fromFiles);
} catch (Exception ex) {
// caught exception, probably because client mappings do not exist
}
if (results != null) {
foreach (var r in results) {
mappings.Add(Utility.NormalizeClientPath(r.LocalPath.Path), r.DepotPath.Path);
}
}
}
return mappings;
}
public string PredictDepotPath(string clientPath) {
var depotPath = string.Empty;
var root = GetConnection().Client.Root;
if (root != null) {
depotPath = clientPath.Replace(root, "/").Replace("\\", "/");
}
return depotPath;
}
public string PredictClientPath(string depotPath) {
var clientPath = string.Empty;
var root = GetConnection().Client.Root;
if (root != null) {
clientPath = Path.Combine(root, depotPath.TrimStart('/').Replace('/', '\\'));
}
return clientPath;
}
public FileSpec GetFileSpec(string path) {
if(path.StartsWith("//")) {
return new FileSpec(new DepotPath(path));
} else {
return new FileSpec(new ClientPath(path));
}
}
public FileSpec[] GetFileSpecs(string[] paths) {
var specs = new FileSpec[paths.Count()];
for (var i = 0; i < paths.Count(); i++) {
specs[i] = GetFileSpec(paths[i]);
}
return specs;
}
///
/// Synchronizes the workspace with the depot
///
/// Performs server-side (db.have) sync
/// Performs a forced synchronization
/// Preview but do not sync
///
public IList SyncWorkspaceFiles(VersionSpec versionSpec = null, bool force = false, bool serverOnly = false, bool preview = false, bool notifyOnError = false) {
return SyncWorkspacePath("//...", versionSpec: versionSpec, force: force, serverOnly: serverOnly, preview: preview, notifyOnError: notifyOnError);
}
public IList SyncWorkspacePath(string path, VersionSpec versionSpec = null, bool force = false, bool serverOnly = false, bool preview = false, bool notifyOnError = false) {
SyncFilesCmdOptions options = null;
if (preview && force && serverOnly) {
options = new SyncFilesCmdOptions((SyncFilesCmdFlags.Preview | SyncFilesCmdFlags.Force | SyncFilesCmdFlags.ServerOnly), -1);
} else if (preview && serverOnly && !force) {
options = new SyncFilesCmdOptions(SyncFilesCmdFlags.Preview | SyncFilesCmdFlags.ServerOnly, -1);
} else if (force && serverOnly && !preview) {
options = new SyncFilesCmdOptions(SyncFilesCmdFlags.Force | SyncFilesCmdFlags.ServerOnly, -1);
} else if (force && preview && !serverOnly) {
options = new SyncFilesCmdOptions(SyncFilesCmdFlags.Force | SyncFilesCmdFlags.Preview, -1);
} else if (force && !serverOnly && !preview) {
options = new SyncFilesCmdOptions(SyncFilesCmdFlags.Force, -1);
} else if (preview && !serverOnly && !force) {
options = new SyncFilesCmdOptions(SyncFilesCmdFlags.Preview, -1);
} else if (serverOnly && !force && !preview) {
options = new SyncFilesCmdOptions(SyncFilesCmdFlags.ServerOnly, -1);
} else {
options = new SyncFilesCmdOptions(SyncFilesCmdFlags.None, -1);
}
FileSpec pathSpec = null;
if (path != null) {
pathSpec = new FileSpec(new DepotPath(path), versionSpec);
}
IList results = null;
try {
results = GetConnection().Client.SyncFiles(options, pathSpec);
} catch (P4Exception ex) {
// sync errors could be displayed to user, but for now they are just silently ignored
// unless 'notifyOnError' is set
if (notifyOnError) {
UIHelper.ShowMessage(string.Format("Error synchronizing from server:\n\n{0}", ex.Message));
}
}
return results;
}
///
/// Performs a p4 add on the specified file
///
/// The client path for the file
///
public IList MarkFileForAdd(params string[] paths) {
var change = GetOrCreatePendingChangelist();
var specs = GetFileSpecs(paths);
var options = new AddFilesCmdOptions(AddFilesCmdFlags.KeepWildcards, change.Id, null);
return GetConnection().Client.AddFiles(options, specs);
}
///
/// Performs a p4 edit on the specified file
///
/// The path(s) (either depot or client path) of the file to be edited
public IList CheckoutFiles(bool serverOnly=false, params string[] paths) {
var change = GetOrCreatePendingChangelist();
var specs = GetFileSpecs(paths);
var flags = EditFilesCmdFlags.None;
if (serverOnly) {
flags = EditFilesCmdFlags.ServerOnly;
}
var options = new EditCmdOptions(flags, change.Id, null);
var client = GetConnection().Client;
return client.EditFiles(options, specs);
}
///
///
///
///
///
///
public IList RenameFile(string sourcePath, string destPath, bool serverOnly=false) {
IList moved = null;
var md = GetFileMetaData(sourcePath);
// some error handling here
// if there is no metadata, then the file hasn't even been added to the server yet, so just exit the method
if (md == null) return null;
// if the file is locked by someone else, then prevent the rename -- we can't do much if the rename occurs in the filesystem
// but we can at least prevent the rename in Perforce
if (!MetadataHelper.CanEdit(md)) return null;
// if the file is deleted in Perforce, then prevent the rename
if (md.Action == FileAction.Delete) return null;
// now check the destination name
var destMd = GetFileMetaData(destPath);
if (destMd != null) {
// if someone else has the destination locked, bail out
if (!MetadataHelper.CanEdit(destMd)) return null;
// if the destination has been deleted previously, then continue otherwise bail out
if (destMd.HeadAction != FileAction.Delete) return null;
}
// mark the file for edit
if (md.Action != FileAction.Add) {
CheckoutFiles(true, sourcePath);
}
// now do the rename
var change = GetOrCreatePendingChangelist();
var sourceFileSpec = GetFileSpec(sourcePath);
var destFileSpec = GetFileSpec(destPath);
var flags = MoveFileCmdFlags.None;
if (serverOnly) {
flags = MoveFileCmdFlags.ServerOnly;
}
var options = new MoveCmdOptions(flags, change.Id, null);
moved = GetConnection().Client.MoveFiles(sourceFileSpec, destFileSpec, options);
return moved;
}
public IList RenameFolder(string sourceDir, string destDir, bool serverOnly = false) {
IList moved = null;
// check to see if the source directory exists
if (!IsDirectory(sourceDir)) {
// directory does not exist, but maybe it is a path with adds...
var md = GetFileMetaData(sourceDir + @"/...", maxItems: 1);
// if we have no metadata, then there's nothing to rename
if (md == null) return null;
// if we aren't seeing ADD actions, then we should bail out too
if (md.Action != FileAction.Add) return null;
}
// check to see if the destination directory exists and bail out if it does
// if we are doing a 'serverOnly' rename, then we want the destDir to exist
var destExists = IsDirectory(destDir);
if (serverOnly && !destExists) return null;
if (!serverOnly && destExists) return null;
// if we got to this point, things look good... now look for any locks
var paths = new List();
paths.Add(sourceDir + @"/...");
var allMD = GetFileMetaData(paths);
var lockFound = false;
foreach (var md in allMD) {
if (!MetadataHelper.CanEdit(md)) {
lockFound = true;
break;
}
}
// bail out if a lock is found anywhere in the renamed directory
if (lockFound) return null;
// if we made it here, let's go ahead and do the rename
var change = GetOrCreatePendingChangelist();
var sourceDirWC = string.Empty;
if (sourceDir.StartsWith("//")) {
sourceDirWC = sourceDir + @"/...";
} else {
sourceDirWC = sourceDir + @"\...";
}
var destDirWC = string.Empty;
if (destDir.StartsWith("//")) {
destDirWC = destDir + @"/...";
} else {
destDirWC = destDir + @"\...";
}
var sourceFileSpec = GetFileSpec(sourceDirWC);
var destFileSpec = GetFileSpec(destDirWC);
CheckoutFiles(serverOnly: true, paths: sourceDirWC);
var flags = MoveFileCmdFlags.None;
if (serverOnly) {
flags = MoveFileCmdFlags.ServerOnly;
}
var options = new MoveCmdOptions(flags, change.Id, null);
moved = GetConnection().Client.MoveFiles(sourceFileSpec, destFileSpec, options);
return moved;
}
public IList CopyFile(string sourcePath, string destPath, bool serverOnly = false) {
IList copied = null;
var md = GetFileMetaData(sourcePath);
// some error handling here
// if there is no metadata, then the file hasn't even been added to the server yet, so just exit the method
if (md == null) return null;
// if the file is locked by someone else, then prevent the rename -- we can't do much if the rename occurs in the filesystem
// but we can at least prevent the rename in Perforce
if (!MetadataHelper.CanEdit(md)) return null;
// if the file is deleted in Perforce, then prevent the rename
if (md.Action == FileAction.Delete) return null;
// now check the destination name
var destMd = GetFileMetaData(destPath);
if (destMd != null) {
// if someone else has the destination locked, bail out
if (!MetadataHelper.CanEdit(destMd)) return null;
// if the destination has been deleted previously, then continue otherwise bail out
if (destMd.HeadAction != FileAction.Delete) return null;
}
var change = GetOrCreatePendingChangelist();
var sourceFileSpec = GetFileSpec(sourcePath);
var destFileSpecList = new List();
destFileSpecList.Add(GetFileSpec(destPath));
var flags = CopyFilesCmdFlags.None;
if(serverOnly) {
flags = CopyFilesCmdFlags.Virtual;
}
var options = new CopyFilesCmdOptions(flags, null, null, null, change.Id, 0);
copied = GetConnection().Client.CopyFiles(sourceFileSpec, destFileSpecList, options);
return copied;
}
///
///
///
///
///
public IList DeleteFiles(bool serverOnly = false, params string[] paths) {
var change = GetOrCreatePendingChangelist();
var specs = GetFileSpecs(paths);
var flags = DeleteFilesCmdFlags.None;
if (serverOnly) {
flags = DeleteFilesCmdFlags.ServerOnly;
}
var options = new DeleteFilesCmdOptions(flags, change.Id);
var results = GetConnection().Client.DeleteFiles(options, specs);
return results;
}
public IList RevertFiles(bool serverOnly = false, params string[] paths) {
var change = GetOrCreatePendingChangelist();
var specs = GetFileSpecs(paths);
var flags = RevertFilesCmdFlags.None;
if (serverOnly) {
flags = RevertFilesCmdFlags.ServerOnly;
}
var options = new RevertCmdOptions(flags, change.Id);
return GetConnection().Client.RevertFiles(options, specs);
}
public P4CommandResult ReconcileFiles(params string[] paths) {
if (paths == null) return null;
var change = GetOrCreatePendingChangelist();
var args = new List();
args.Add("-ead");
args.Add("-c");
args.Add(change.Id.ToString());
foreach (var p in paths) {
args.Add(p);
}
var cmd = new P4Command(GetRepository(), "reconcile", true, args.ToArray());
return cmd.Run();
}
public IList ShelveFiles(params string[] paths) {
var change = GetOrCreatePendingChangelist();
var specs = GetFileSpecs(paths);
var options = new ShelveFilesCmdOptions(ShelveFilesCmdFlags.Force, null, change.Id);
var results = GetConnection().Client.ShelveFiles(options, specs);
return results;
}
public IList DeleteShelvedFiles(int changeId = 0, params string[] paths) {
if (changeId == 0) {
var change = GetOrCreatePendingChangelist();
changeId = change.Id;
}
var specs = GetFileSpecs(paths);
var options = new ShelveFilesCmdOptions(ShelveFilesCmdFlags.Delete, null, changeId);
return GetConnection().Client.ShelveFiles(options, specs);
}
public List GetShelvedLocations(string depotPath) {
var changeIds = new List();
var changes = GetAllPendingChangelists();
foreach (var c in changes) {
if (c.Shelved) {
var change = GetChangelist(c.Id, includeShelvedFiles: true);
if (change.ShelvedFiles != null) {
foreach (var sf in change.ShelvedFiles) {
if (sf.Path.Path.Equals(depotPath, StringComparison.CurrentCultureIgnoreCase)) {
changeIds.Add(c.Id);
break;
}
}
}
}
}
return changeIds;
}
// this method should look at all pending changelists for this workspace to see if the file is shelved anywhere
public bool IsFileShelved(string depotPath) {
var shelved = false;
var shelveIds = GetShelvedLocations(depotPath);
if (shelveIds.Count > 0) {
shelved = true;
}
return shelved;
}
public IList UnshelveFiles(int changeId = 0, params string[] paths) {
var change = GetCurrentPendingChangelist();
if (changeId == 0) {
changeId = change.Id;
}
var specs = GetFileSpecs(paths);
var options = new UnshelveFilesCmdOptions(UnshelveFilesCmdFlags.Force, changeId, change.Id);
var results = GetConnection().Client.UnshelveFiles(options, specs);
return results;
}
public bool IsFileOpened(string depotPath) {
var opened = false;
if (!string.IsNullOrEmpty(depotPath)) {
var spec = GetFileSpec(depotPath);
var specs = new List();
specs.Add(spec);
var options = new GetOpenedFilesOptions(GetOpenedFilesCmdFlags.None, null, CurrentClient.Name, Username, 0);
var list = GetRepository().GetOpenedFiles(specs, options);
if(list != null && list.Count > 0) {
foreach (var f in list) {
if(f.DepotPath.Path.Equals(depotPath, StringComparison.CurrentCultureIgnoreCase)) {
opened = true;
break;
}
}
}
}
return opened;
}
public bool PathHasAnyOpened(string depotPath) {
var anyOpened = false;
if (!string.IsNullOrEmpty(depotPath)) {
var results = GetAllOpened(depotPath);
if (results != null && results.Count > 0) {
foreach (var file in results) {
if (file.Action == FileAction.Add) continue;
anyOpened = true;
}
}
}
return anyOpened;
}
public IList GetAllOpened(string depotPath, int max=0) {
var fileSpecs = new List();
fileSpecs.Add(GetFileSpec(depotPath));
var options = new GetOpenedFilesOptions(GetOpenedFilesCmdFlags.AllClients, null, null, null, max);
return GetRepository().GetOpenedFiles(fileSpecs, options);
}
public FileMetaData GetFileMetaData(PathSpec path, int revision, int maxItems = 0) {
FileMetaData md = null;
var filespec = new FileSpec(path, new Revision(revision));
var flags = GetFileMetadataCmdFlags.Attributes | GetFileMetadataCmdFlags.FileSize | GetFileMetadataCmdFlags.HexAttributes;
var options = new GetFileMetaDataCmdOptions(flags, null, null, maxItems, null, null, null);
var metaDataList = GetRepository().GetFileMetaData(options, filespec);
if (metaDataList != null && metaDataList.Count > 0) {
md = metaDataList[0];
}
return md;
}
public FileMetaData GetFileMetaData(string path, string attrFilter = null, bool allVersions = false, int maxItems = 0) {
FileMetaData md = null;
var paths = new List();
paths.Add(path);
var results = GetFileMetaData(paths, attrFilter, allVersions, maxItems);
if (results != null && results.Count == 1) {
md = results[0];
}
return md;
}
///
///
///
///
///
///
///
public IList GetFileMetaData(List paths, string attrFilter = null, bool allVersions = false, int maxItems = 0) {
IList results = new List();
if (paths == null || paths.Count == 0 || paths[0] == null) {
return results;
}
IList specs = new List();
foreach (var path in paths) {
if (path.Equals(Constants.DUMMY_DEPOT_PATH)) {
continue;
}
// figure out if this is a depot path or client path
// if it starts with //, assume a depot path; client path otherwise
if(path.StartsWith("//")) {
specs.Add(new FileSpec(new DepotPath(path), null));
} else {
specs.Add(new FileSpec(new ClientPath(path), null));
}
}
if (specs.Count == 0) return null;
GetFileMetadataCmdFlags flags = GetFileMetadataCmdFlags.None;
if (allVersions) {
flags = GetFileMetadataCmdFlags.Attributes | GetFileMetadataCmdFlags.FileSize | GetFileMetadataCmdFlags.HexAttributes | GetFileMetadataCmdFlags.AllRevisions;
} else {
flags = GetFileMetadataCmdFlags.Attributes | GetFileMetadataCmdFlags.FileSize | GetFileMetadataCmdFlags.HexAttributes;
}
var options = new GetFileMetaDataCmdOptions(flags, null, null, maxItems, null, null, attrFilter);
try {
results = GetRepository().GetFileMetaData(specs, options);
} catch (Exception ex) {
// unable to get metadata -- can happen when bad specs are passed. need to handle gracefully (rather than crashing)
}
return results;
}
public IList GetSearchFileMetaData(List paths, bool mappedOnly = false) {
GetFileMetadataCmdFlags flags = GetFileMetadataCmdFlags.Attributes | GetFileMetadataCmdFlags.HexAttributes;
IList specs = new List();
foreach(var p in paths) {
var fs = new FileSpec(new DepotPath(p), null);
specs.Add(fs);
}
var filter = new StringBuilder();
filter.Append("headRev & ^headAction=delete & ^headAction=move/delete");
if (mappedOnly) {
filter.Append(" & isMapped");
}
var options = new GetFileMetaDataCmdOptions(flags, filter.ToString(), null, -1, null, null, "tags");
return GetRepository().GetFileMetaData(specs, options);
}
public SizeData GetPathSizes(string depotPath) {
SizeData data = null;
if (IsDirectory(depotPath) && !depotPath.EndsWith("/...")) {
depotPath = depotPath.TrimEnd('/') + "/...";
}
var args = new List();
args.Add("-s");
args.Add(depotPath);
var cmd = new P4Command(GetRepository(), "sizes", true, args.ToArray());
var results = cmd.Run();
if (results.TaggedOutput != null) {
data = new SizeData();
foreach (TaggedObject obj in results.TaggedOutput) {
if (obj.ContainsKey("path")) {
data.Path = obj["path"].ToString();
}
if (obj.ContainsKey("fileCount")) {
data.FileCount = int.Parse(obj["fileCount"].ToString());
}
if (obj.ContainsKey("fileSize")) {
data.FileSize = long.Parse(obj["fileSize"].ToString());
}
}
}
return data;
}
public string GetFileFromServer(string depotPath, int revision = -1) {
string filePath = null;
if (depotPath != null) {
var extIndex = depotPath.LastIndexOf('.');
if (extIndex < depotPath.Length) {
var extension = depotPath.Substring(extIndex);
var path = Path.GetTempPath();
var fileName = Guid.NewGuid().ToString() + extension;
filePath = Path.Combine(path, fileName);
var options = new GetFileContentsCmdOptions(GetFileContentsCmdFlags.None, filePath);
FileSpec spec = null;
if (revision > 0) {
spec = FileSpec.DepotSpec(depotPath, revision);
} else if(revision == 0) {
spec = new FileSpec(new DepotPath(depotPath), VersionSpec.None);
} else {
spec = new FileSpec(new DepotPath(depotPath), VersionSpec.Head);
}
var results = GetRepository().GetFileContents(options, spec);
}
}
return filePath;
}
public string GetDirectoryFromServer(string depotPath, string targetDir = null) {
Log.Debug(string.Format("GetDirectoryFromServer {0} -> {1}", depotPath, targetDir));
if (depotPath != null) {
depotPath = depotPath.TrimEnd('.').TrimEnd('/');
var dirname = depotPath.Substring(depotPath.LastIndexOf('/') + 1);
var parentDepotPath = depotPath.Substring(0, depotPath.LastIndexOf('/') + 1);
if(string.IsNullOrEmpty(targetDir)) {
targetDir = Path.GetTempPath();
}
var dirSpec = GetFileSpec(depotPath + "/...");
var filesOptions = new FilesCmdOptions(FilesCmdFlags.None, 0);
var files = GetRepository().GetFiles(filesOptions, dirSpec);
foreach (var file in files) {
var fileDepotPath = file.DepotPath.Path;
var subPath = fileDepotPath.Replace(parentDepotPath, "").Replace('/', '\\');
var outputFilePath = Path.Combine(targetDir, subPath);
var getFileContentsOptions = new GetFileContentsCmdOptions(GetFileContentsCmdFlags.None, outputFilePath);
var fileSpec = GetFileSpec(fileDepotPath);
var results = GetRepository().GetFileContents(getFileContentsOptions, fileSpec);
}
}
return targetDir;
}
public string GetFileFromShelf(string depotPath, int changeId = -1) {
string filePath = null;
if (depotPath != null) {
if (changeId < 1) {
changeId = GetCurrentPendingChangelist().Id;
}
var extIndex = depotPath.LastIndexOf('.');
if (extIndex < depotPath.Length) {
var extension = depotPath.Substring(extIndex);
var path = Path.GetTempPath();
var fileName = Guid.NewGuid().ToString() + extension;
filePath = Path.Combine(path, fileName);
var shelf = new ShelvedInChangelistIdVersion(changeId);
var options = new GetFileContentsCmdOptions(GetFileContentsCmdFlags.None, filePath);
var spec = new FileSpec(new DepotPath(depotPath), shelf);
var results = GetRepository().GetFileContents(options, spec);
}
}
return filePath;
}
public IList GetFileHistory(string path) {
var flags = GetFileHistoryCmdFlags.IncludeInherited|GetFileHistoryCmdFlags.FullDescription;
var fileSpec = GetFileSpec(path);
var options = new GetFileHistoryCmdOptions(flags, 0, 0);
return GetRepository().GetFileHistory(options, fileSpec);
}
public SubmitResults RollbackFileToRevision(string path, int revision) {
var fileSpec = FileSpec.DepotSpec(path, revision);
var desc = string.Format("Rolling file {0} back to revision {1}", path, revision);
var change = CreateChangelist(desc);
var mdOptions = new GetFileMetaDataCmdOptions(GetFileMetadataCmdFlags.None, null, null, -1, null, null, null);
var mdList = GetRepository().GetFileMetaData(mdOptions, FileSpec.DepotSpec(path));
if (mdList.Count != 1) return null;
var md = mdList[0];
if (md.HeadRev <= revision) return null;
// sync the desired revision of the file
var syncOptions = new SyncFilesCmdOptions(SyncFilesCmdFlags.Force, 0);
GetConnection().Client.SyncFiles(syncOptions, fileSpec);
// edit the file
var editOptions = new EditCmdOptions(EditFilesCmdFlags.None, change.Id, null);
GetConnection().Client.EditFiles(editOptions, FileSpec.DepotSpec(path));
// sync the head revision of the file
GetConnection().Client.SyncFiles(syncOptions, FileSpec.DepotSpec(path));
// resolve the file (ay)
var resolveOptions = new ResolveCmdOptions(ResolveFilesCmdFlags.AutomaticYoursMode, change.Id);
GetConnection().Client.ResolveFiles(resolveOptions, fileSpec);
// submit the changes
var clientSubmitOptions = new ClientSubmitOptions();
clientSubmitOptions.SubmitType = SubmitType.RevertUnchanged;
var submitOptions = new SubmitCmdOptions(SubmitFilesCmdFlags.None, change.Id, null, null, clientSubmitOptions);
try {
return GetConnection().Client.SubmitFiles(submitOptions, null);
} catch (Exception ex) {
var deleteChangelistOptions = new ChangeCmdOptions(ChangeCmdFlags.None);
GetRepository().DeleteChangelist(change, deleteChangelistOptions);
return null;
}
}
public SubmitResults RollbackFolderToChangelist(string path, int changeId) {
// add wildcard to the path if it isn't there
if (!path.EndsWith("/...")) {
path = path + "/...";
}
// create a changelist
var desc = string.Format("Rolling folder {0} back to change @{1}", path, changeId);
var change = CreateChangelist(desc);
// create a filespec with the changeID
var fileSpec = new FileSpec(new DepotPath(path), new ChangelistIdVersion(changeId));
var deletedList = new List();
var addedList = new List();
var updatedList = new List();
// preview the sync
string[] previewArgs = { "-f", "-n", fileSpec.ToString() };
var p4cmd = GetConnection().CreateCommand("sync", true, previewArgs);
var p4cmdResults = p4cmd.Run();
if (p4cmdResults.TaggedOutput != null) {
foreach (TaggedObject obj in p4cmdResults.TaggedOutput) {
string depotFile = null;
var res = obj.TryGetValue("depotFile", out depotFile);
if (depotFile != null) {
var depotFileSpec = GetFileSpec(depotFile);
if (obj.ContainsKey("action")) {
var action = obj["action"].ToString();
if (action.Equals("deleted")) {
deletedList.Add(depotFileSpec);
} else if (action.Equals("added")) {
addedList.Add(depotFileSpec);
} else if (action.Equals("updated")) {
updatedList.Add(depotFileSpec);
}
}
}
}
}
// sync to the desired changelist
var syncOptions1 = new SyncFilesCmdOptions(SyncFilesCmdFlags.Force, 0);
var syncResults1 = GetConnection().Client.SyncFiles(syncOptions1, fileSpec);
if (updatedList.Count > 0) {
// edit the files in the path
var editOptions = new EditCmdOptions(EditFilesCmdFlags.None, change.Id, null);
var editResults = GetConnection().Client.EditFiles(editOptions, updatedList.ToArray());
// sync the head revision of the file
var syncResults2 = GetConnection().Client.SyncFiles(syncOptions1, updatedList.ToArray());
// resolve the file (ay)
var resolveOptions = new ResolveCmdOptions(ResolveFilesCmdFlags.AutomaticYoursMode, change.Id);
var resolveResults = GetConnection().Client.ResolveFiles(resolveOptions, updatedList.ToArray());
}
if (addedList.Count > 0) {
// find any deleted items that need to be re-added with the -d (downgrade) flag
var addOptions = new AddFilesCmdOptions(AddFilesCmdFlags.Downgrade | AddFilesCmdFlags.KeepWildcards, change.Id, null);
var results = GetConnection().Client.AddFiles(addOptions, addedList.ToArray());
}
if (deletedList.Count > 0) {
// delete any items that aren't supposed to be there
var deleteOptions = new DeleteFilesCmdOptions(DeleteFilesCmdFlags.DeleteUnsynced, change.Id);
var results = GetConnection().Client.DeleteFiles(deleteOptions, deletedList.ToArray());
}
// submit the changes
var clientSubmitOptions = new ClientSubmitOptions();
clientSubmitOptions.SubmitType = SubmitType.SubmitUnchanged;
var submitOptions = new SubmitCmdOptions(SubmitFilesCmdFlags.None, change.Id, null, null, clientSubmitOptions);
try {
var submitResult = GetConnection().Client.SubmitFiles(submitOptions, null);
return submitResult;
} catch (Exception ex) {
var deleteChangelistOptions = new ChangeCmdOptions(ChangeCmdFlags.None);
GetRepository().DeleteChangelist(change, deleteChangelistOptions);
throw new ApplicationException(ex.Message);
}
}
#endregion
#region CONSTRUCTORS
public PerforceHelper(string serverUri, string username) {
_serverUri = serverUri;
_username = username;
_server = null;
var cache = MemoryCache.Default;
cache.Remove(REPO_CACHE_KEY);
var repo = GetRepository(useCache: false, testConnection: false);
var addr = repo.Server.Address;
if(repo.Connection == null) {
throw new ApplicationException("ERROR: unable to connect to server");
}
}
#endregion
#region ACCESSOR FUNCTIONS
public string Username {
get { return _username; }
set { _username = value; }
}
public string ServerURI {
get { return _serverUri; }
set { _serverUri = value; }
}
public string Password {
set { _password = value; }
}
public string Ticket {
get { return _ticket; }
set { _ticket = value; }
}
public bool ClientEnabled {
get {
var enabled = false;
if (CurrentClient != null) {
enabled = true;
}
return enabled;
}
}
#endregion
#region PRIVATE COMMUNICATION METHODS
private Server _server;
private Server GetServer() {
try {
if (_server == null) {
_server = new Server(new ServerAddress(_serverUri));
}
return _server;
} catch (Exception e) {
throw new P4HelperException(e.Message);
}
}
private static string REPO_CACHE_KEY = "repository";
private Repository GetRepository(bool useCache = true, bool testConnection = false) {
ObjectCache cache = null;
Repository repo = null;
if (testConnection) {
// test the connection with a 5-second timeout. Why 5 seconds? no real
// reason. it had to be some timeout value, and 5 seconds seemed a reasonably
// short time to wait without having false positives.
if (!Utility.CheckTCPConnection(_serverUri, timeout: 5)) {
var exception = new P4HelperException("Cannot reach the server");
UIHelper.CriticalError(exception);
}
}
if (useCache) {
cache = MemoryCache.Default;
repo = (Repository)cache.Get(REPO_CACHE_KEY);
}
if (repo == null) {
repo = new Repository(GetServer());
repo.Connection.UserName = _username;
if (_client != null) {
repo.Connection.SetClient(_client.Name);
}
var options = new Options();
repo.Connection.Connect(options);
repo.Connection.CommandTimeout = TimeSpan.FromMinutes(20);
if (useCache) {
var policy = new CacheItemPolicy();
policy.SlidingExpiration = TimeSpan.FromSeconds(30.0);
cache.Add(REPO_CACHE_KEY, repo, policy);
}
}
return repo;
}
private Connection GetConnection(bool useCache = true, bool testConnection = false) {
Connection con = null;
var repo = GetRepository(useCache, testConnection);
if (repo != null) {
con = repo.Connection;
}
return con;
}
#endregion
[Conditional("DEBUG")]
public static void Debug(String logMessage) {
_log.Debug(logMessage);
}
#region CLEANUP
protected virtual void Dispose(bool disposing) {
if (disposing) {
// dispose managed resources
GetRepository().Dispose();
}
// free native resources
}
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
}
#region EXCEPTION CLASS
[Serializable]
public class P4HelperException : Exception, ISerializable {
public P4HelperException(string msg) : base(msg) {
}
}
#endregion
}