// Perforce Revert this file public void RevertCall(object sender) { Perforce.RevertFiles(new List <string>() { ItemPath }); UpdateFileStatus(); }
// Perforce Checkout this file public void CheckoutCall(object sender) { Perforce.CheckoutFiles(new List <string>() { ItemPath }); UpdateFileStatus(); }
// Perforce Delete this file public void DeleteCall(object sender) { Perforce.DeleteFiles(new List <string>() { ItemPath }); UpdateFileStatus(); }
//------------------------------------------------------------------------- private void LoadFromP4() { // Get users from P4. string output = Perforce.RunCommand("users"); // Split the output into individual lines. string[] lines = output.Split(new string[] { Environment.NewLine }, StringSplitOptions.None); // The username is the first thing on each line followed by a space char. foreach (string line in lines) { if (line.IndexOf(' ') < 0) { continue; } string username = line.Substring(0, line.IndexOf(' ')); //Program.Log.AddEntry( // Log.EntryType.INFO, // "Found user '" + username + "' in P4." ); try { if (GetUser(username) != null) { continue; } User user = new User( false, username, "Unknown", "Unknown", "123454321", "Unknown", false, false, false); Users.Add(user); //Program.Log.AddEntry( // Log.EntryType.INFO, // "Added user '" + user.Username + "' to DB." ); } catch (Exception ex) { Program.Log.AddEntry( Log.EntryType.ERROR, "Error while creating a user: " + ex.Message); } } }
void PollForUpdates() { while (!QuitEvent.WaitOne(5 * 60 * 1000)) { StringWriter Log = new StringWriter(); List <PerforceChangeSummary> Changes; if (Perforce.FindChanges(WatchPath, 1, out Changes, Log) && Changes.Count > 0) { TriggerUpdate(UpdateType.Background, null); } } }
// Updates the Perforce status of the file public static void UpdatePerforceStatus(List <UserFile> fileList) { string[] fileStatusList = fileList.Select(x => x.ItemPath).ToArray(); if (fileStatusList.Any()) { List <FileMetaData> p4Info = Perforce.FileStatus(fileStatusList); if (p4Info != null) { foreach (FileMetaData metaData in p4Info) { UserFile updateFile = fileList.Where(x => x.ItemPath == metaData.LocalPath.Path).FirstOrDefault(); try { if (metaData == null) { updateFile.P4Status = P4Status.Unknown; } else if (metaData.OtherUsers != null) { updateFile.P4Status = P4Status.CheckedOutOther; } else if (metaData.Action == FileAction.None) { updateFile.P4Status = P4Status.CheckedIn; } else if (metaData.Action == FileAction.Edit) { updateFile.P4Status = P4Status.CheckedOut; } else if (metaData.Action == FileAction.Add) { updateFile.P4Status = P4Status.Add; } else if (metaData.Action == FileAction.Delete) { updateFile.P4Status = P4Status.Delete; } updateFile.P4Success = true; } catch (Exception e) { updateFile.P4Status = P4Status.Unknown; updateFile.P4Success = false; updateFile.FileError = e; System.Windows.Clipboard.SetText(updateFile.FileError.ToString()); } } } } }
//------------------------------------------------------------------------- private void uiChangelists_SelectedIndexChanged(object sender, EventArgs e) { // Reset form section. uiChangelistFiles.Items.Clear(); uiChangelistNumber.Text = ""; uiChangelistUser.Text = ""; uiChangelistDate.Text = ""; // Get the selected changelist. Changelist changelist = uiChangelists.SelectedItem as Changelist; if (changelist == null) { return; } uiChangelistNumber.Text = changelist.Id.ToString(); uiChangelistUser.Text = changelist.Submitter.Username; uiChangelistDate.Text = changelist.SubmittedDate.ToString("yyyy/MM/dd"); //-- Get changelist's files from P4. string output = Perforce.RunCommand("describe -s " + changelist.Id); // Exract files from output. int index = output.IndexOf("Affected files ..."); while ((index = output.IndexOf("... ", index + 1)) > -1) { // Skip the "... " prefixing the path. index += 4; // Grab the path from between the ellipses and the revision. int revisionIndex = output.IndexOf(' ', index); if (revisionIndex < 0) { continue; } string path = output.Substring(index, revisionIndex - index); // Add file path to the ui list. uiChangelistFiles.Items.Add(path); } }
// Update the Perforce status of the file, and whether or not Perforce successfully checked the file public virtual void UpdatePerforceStatus() { // If Perforce is Down or errors on getting file status fail gracefully and set P4 status to unknown try { List <FileMetaData> p4Info = Perforce.FileStatus(ItemPath); if (p4Info == null) { P4Status = P4Status.Unknown; } else if (p4Info.FirstOrDefault().OtherUsers != null) { P4Status = P4Status.CheckedOutOther; } else if (p4Info.FirstOrDefault().Action == FileAction.None) { P4Status = P4Status.CheckedIn; } else if (p4Info.FirstOrDefault().Action == FileAction.Edit) { P4Status = P4Status.CheckedOut; } else if (p4Info.FirstOrDefault().Action == FileAction.Add) { P4Status = P4Status.Add; } else if (p4Info.FirstOrDefault().Action == FileAction.Delete) { P4Status = P4Status.Delete; } P4Success = true; } catch (Exception e) { P4Status = P4Status.Unknown; P4Success = false; FileError = e; } }
public bool Run(out string ErrorMessage) { PerforceConnection Perforce = new PerforceConnection(null, null, null); // Get the P4PORT setting in this folder, so we can respect the contents of any P4CONFIG file string PrevDirectory = Directory.GetCurrentDirectory(); try { Directory.SetCurrentDirectory(Path.GetDirectoryName(NewSelectedFileName)); string ServerAndPort; if (Perforce.GetSetting("P4PORT", out ServerAndPort, Log)) { Perforce = new PerforceConnection(null, null, ServerAndPort); } } finally { Directory.SetCurrentDirectory(PrevDirectory); } // Get the Perforce server info PerforceInfoRecord PerforceInfo; if (!Perforce.Info(out PerforceInfo, Log)) { ErrorMessage = String.Format("Couldn't get Perforce server info"); return(false); } if (String.IsNullOrEmpty(PerforceInfo.UserName)) { ErrorMessage = "Missing user name in call to p4 info"; return(false); } if (String.IsNullOrEmpty(PerforceInfo.HostName)) { ErrorMessage = "Missing host name in call to p4 info"; return(false); } ServerTimeZone = PerforceInfo.ServerTimeZone; // Find all the clients on this machine Log.WriteLine("Enumerating clients on local machine..."); List <PerforceClientRecord> Clients; if (!Perforce.FindClients(out Clients, Log)) { ErrorMessage = String.Format("Couldn't find any clients for this host."); return(false); } // Find any clients which are valid. If this is not exactly one, we should fail. List <PerforceConnection> CandidateClients = new List <PerforceConnection>(); foreach (PerforceClientRecord Client in Clients) { // Make sure the client is well formed if (!String.IsNullOrEmpty(Client.Name) && (!String.IsNullOrEmpty(Client.Host) || !String.IsNullOrEmpty(Client.Owner)) && !String.IsNullOrEmpty(Client.Root)) { // Require either a username or host name match if ((String.IsNullOrEmpty(Client.Host) || String.Compare(Client.Host, PerforceInfo.HostName, StringComparison.InvariantCultureIgnoreCase) == 0) && (String.IsNullOrEmpty(Client.Owner) || String.Compare(Client.Owner, PerforceInfo.UserName, StringComparison.InvariantCultureIgnoreCase) == 0)) { if (!Utility.SafeIsFileUnderDirectory(NewSelectedFileName, Client.Root)) { Log.WriteLine("Rejecting {0} due to root mismatch ({1})", Client.Name, Client.Root); continue; } PerforceConnection CandidateClient = new PerforceConnection(PerforceInfo.UserName, Client.Name, Perforce.ServerAndPort); bool bFileExists; if (!CandidateClient.FileExists(NewSelectedFileName, out bFileExists, Log) || !bFileExists) { Log.WriteLine("Rejecting {0} due to file not existing in workspace", Client.Name); continue; } List <PerforceFileRecord> Records; if (!CandidateClient.Stat(NewSelectedFileName, out Records, Log)) { Log.WriteLine("Rejecting {0} due to {1} not in depot", Client.Name, NewSelectedFileName); continue; } Records.RemoveAll(x => !x.IsMapped); if (Records.Count == 0) { Log.WriteLine("Rejecting {0} due to {1} matching records", Client.Name, Records.Count); continue; } Log.WriteLine("Found valid client {0}", Client.Name); CandidateClients.Add(CandidateClient); } } } // Check there's only one client if (CandidateClients.Count == 0) { ErrorMessage = String.Format("Couldn't find any Perforce workspace containing {0}.", NewSelectedFileName); return(false); } else if (CandidateClients.Count > 1) { ErrorMessage = String.Format("Found multiple workspaces containing {0}:\n\n{1}\n\nCannot determine which to use.", Path.GetFileName(NewSelectedFileName), String.Join("\n", CandidateClients.Select(x => x.ClientName))); return(false); } // Take the client we've chosen PerforceClient = CandidateClients[0]; // Get the client path for the project file if (!PerforceClient.ConvertToClientPath(NewSelectedFileName, out NewSelectedClientFileName, Log)) { ErrorMessage = String.Format("Couldn't get client path for {0}", NewSelectedFileName); return(false); } // Figure out where the engine is in relation to it for (int EndIdx = NewSelectedClientFileName.Length - 1;; EndIdx--) { if (EndIdx < 2) { ErrorMessage = String.Format("Could not find engine in Perforce relative to project path ({0})", NewSelectedClientFileName); return(false); } if (NewSelectedClientFileName[EndIdx] == '/') { bool bFileExists; if (PerforceClient.FileExists(NewSelectedClientFileName.Substring(0, EndIdx) + "/Engine/Source/UE4Editor.target.cs", out bFileExists, Log) && bFileExists) { BranchClientPath = NewSelectedClientFileName.Substring(0, EndIdx); break; } } } Log.WriteLine("Found branch root at {0}", BranchClientPath); // Get the local branch root if (!PerforceClient.ConvertToLocalPath(BranchClientPath + "/Engine/Source/UE4Editor.target.cs", out BaseEditorTargetPath, Log)) { ErrorMessage = String.Format("Couldn't get local path for editor target file"); return(false); } // Find the editor target for this project if (NewSelectedFileName.EndsWith(".uproject", StringComparison.InvariantCultureIgnoreCase)) { List <PerforceFileRecord> Files; if (PerforceClient.FindFiles(PerforceUtils.GetClientOrDepotDirectoryName(NewSelectedClientFileName) + "/Source/*Editor.Target.cs", out Files, Log) && Files.Count >= 1) { PerforceFileRecord File = Files.FirstOrDefault(x => x.Action == null || !x.Action.Contains("delete")); if (File == null) { Log.WriteLine("Couldn't find any non-deleted editor targets for this project."); } else { string DepotPath = File.DepotPath; NewProjectEditorTarget = Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(DepotPath.Substring(DepotPath.LastIndexOf('/') + 1))); Log.WriteLine("Using {0} as editor target name (from {1})", NewProjectEditorTarget, Files[0]); } } else { Log.WriteLine("Couldn't find any editor targets for this project."); } } // Get a unique name for the project that's selected. For regular branches, this can be the depot path. For streams, we want to include the stream name to encode imports. if (PerforceClient.GetActiveStream(out StreamName, Log)) { string ExpectedPrefix = String.Format("//{0}/", PerforceClient.ClientName); if (!NewSelectedClientFileName.StartsWith(ExpectedPrefix, StringComparison.InvariantCultureIgnoreCase)) { ErrorMessage = String.Format("Unexpected client path; expected '{0}' to begin with '{1}'", NewSelectedClientFileName, ExpectedPrefix); return(false); } string StreamPrefix; if (!TryGetStreamPrefix(PerforceClient, StreamName, Log, out StreamPrefix)) { ErrorMessage = String.Format("Failed to get stream info for {0}", StreamName); return(false); } NewSelectedProjectIdentifier = String.Format("{0}/{1}", StreamPrefix, NewSelectedClientFileName.Substring(ExpectedPrefix.Length)); } else { if (!PerforceClient.ConvertToDepotPath(NewSelectedClientFileName, out NewSelectedProjectIdentifier, Log)) { ErrorMessage = String.Format("Couldn't get depot path for {0}", NewSelectedFileName); return(false); } } // Read the project logo if (NewSelectedFileName.EndsWith(".uproject", StringComparison.InvariantCultureIgnoreCase)) { string LogoFileName = Path.Combine(Path.GetDirectoryName(NewSelectedFileName), "Build", "UnrealGameSync.png"); if (File.Exists(LogoFileName)) { try { // Duplicate the image, otherwise we'll leave the file locked using (Image Image = Image.FromFile(LogoFileName)) { ProjectLogo = new Bitmap(Image); } } catch { ProjectLogo = null; } } } // Succeeed! ErrorMessage = null; return(true); }
//------------------------------------------------------------------------- // Processes diff content produced using "p4 diff2 -du file1 file2". public static string GetHtmlDiffContent( string p4filePath, int revision) { //-- Get the complete prev revision. string prevContent = Perforce.RunCommand( "print -q " + p4filePath + '#' + ((revision > 0) ? revision - 1 : revision)); // Make it html safe (replace the angle brackets). prevContent = prevContent.Replace("<", "<"); prevContent = prevContent.Replace(">", ">"); // Split into individual lines. string[] tmpPrevContentLines = prevContent.Split(new string[] { Environment.NewLine }, StringSplitOptions.None); List <string> prevContentLines = new List <string>(); foreach (string line in tmpPrevContentLines) { prevContentLines.Add(line); } //-- Perform the diff. string diffContent = Perforce.RunCommand( "diff2 -du " + p4filePath + '#' + (revision - 1) + ' ' + p4filePath + '#' + revision); // Make HTML safe (replace the angle brackets). diffContent = diffContent.Replace("<", "<"); diffContent = diffContent.Replace(">", ">"); string[] lines = diffContent.Split(new string[] { Environment.NewLine }, StringSplitOptions.None); // Iterate through the diff's lines, applying changes to the previous revision as we go. int insertPoint = 0; int linesAdded = 0; foreach (string line in lines) { // Is this line the beginning of a diff section? if (line.IndexOf("@@") == 0) { // Get the line number that the diff applies to. string tmp = line.Remove(0, 4); string lineNumberStr = tmp.Substring(0, tmp.IndexOf(',')); if (int.TryParse(lineNumberStr, out insertPoint) == false) { throw new Exception("Failed to extract line number from diff."); } // Translate insert-point from range start at 1 to range starting at 0. insertPoint--; insertPoint += linesAdded; continue; } if (line.Length > 0) { // Content was added. if (line[0] == '+') { if (insertPoint > prevContentLines.Count) { throw new Exception("Insert-point is out-of-bounds."); } string tmp = line.Remove(0, 1); prevContentLines.Insert( insertPoint, "<font bgcolor='" + c_changeColour_addition + "'>" + tmp + "</font>"); linesAdded++; } // Content was removed. else if (line[0] == '-') { if (insertPoint > prevContentLines.Count) { throw new Exception("Insert-point is out-of-bounds."); } string tmp = line.Remove(0, 1); while (tmp.Length < 80) { tmp += ' '; } prevContentLines.RemoveAt(insertPoint); prevContentLines.Insert(insertPoint, "<font bgcolor='" + c_changeColour_subtraction + "'>" + tmp + "</font>"); } insertPoint++; } } // Create a line-number format string - we want to pad the line number // with enough zeroes so all line numbers in this file have the same // number of digits. int lineNumber = 1; string lineNumberFormat = ""; for (int i = 0; i < prevContentLines.Count.ToString().Length; i++) { lineNumberFormat += '0'; } // Create html. string html = "<html>" + Environment.NewLine + "<head></head>" + Environment.NewLine + "<body>" + Environment.NewLine; foreach (string line in prevContentLines) { // TODO: Line numbering is wrong. html += /*lineNumber.ToString( lineNumberFormat ) +*/ " | " + line + "<br />" + Environment.NewLine; lineNumber++; } html += Environment.NewLine + "</body>" + Environment.NewLine + "</html>"; // Highlight tab chars. // TODO: This doesn't seem to work. html = html.Replace("" + System.Windows.Forms.Keys.Tab, "<font bgcolor='red'>_</font>"); // Replace spaces with '@nbsp;' (exclude tags). string htmlCopy = html; bool isAngleBrackOpen = false; int htmlOffset = 0; for (int i = 0; i < htmlCopy.Length; i++) { if (htmlCopy[i] == '<') { isAngleBrackOpen = true; } else if (htmlCopy[i] == '>') { isAngleBrackOpen = false; } else if (htmlCopy[i] == ' ' && isAngleBrackOpen == false) { html = html.Remove(htmlOffset, 1); html = html.Insert(htmlOffset, " "); htmlOffset += " ".Length; continue; } htmlOffset++; } html = html.Insert( html.IndexOf("</head>"), "<style>body { white-space: pre; font-family: Courier New; font-size: 100%; }</style>"); return(html); }
//------------------------------------------------------------------------- public static void GetChangelistsFromP4( DateTime fromDate, DateTime toDate, ref List <Changelist> changelists) { string output = Perforce.RunCommand( "changes -s submitted @" + fromDate.ToString("yyyy/MM/dd:00:00") + ",@" + toDate.ToString("yyyy/MM/dd:23:59")); string[] lines = output.Split(new string[] { Environment.NewLine }, StringSplitOptions.None); foreach (string line in lines) { // Extract the changelist number. if (line.Length < "Change ".Length) { continue; } string tmp = line.Remove(0, "Change ".Length); int index = tmp.IndexOf(' '); if (index < 0) { continue; } string changelistNumberStr = tmp.Substring(0, index); int changelistNumber; if (int.TryParse(changelistNumberStr, out changelistNumber) == false) { continue; } // Exract the date. tmp = tmp.Remove(0, (changelistNumberStr + " on ").Length); string dateStr = tmp.Substring(0, 10); DateTime date = new DateTime(); if (DateTime.TryParse(dateStr, out date) == false) { continue; } // Extract the user. tmp = tmp.Remove(0, (dateStr + " by ").Length); string username = tmp.Substring(0, tmp.IndexOf('@')); User user = Program.UserCollection.GetUser(username); if (user == null || user.IsReviewCandidate == false) { continue; } // Extract the description. int descriptionStartIndex = tmp.IndexOf("'") + 1; int descriptionLength = tmp.LastIndexOf("'") - descriptionStartIndex; string description = ""; if (descriptionStartIndex > 0 && descriptionLength > 0) { description = tmp.Substring(descriptionStartIndex, descriptionLength); } if (description.Length == 0) { description = "(No description)"; } //if( description.Length > 30 ) //{ // description = description.TrimEnd() + "..."; //} // Create the changelist object if we don't already have one. if (Changelists.ContainsKey(changelistNumber) == false) { changelists.Add( new Changelist( changelistNumber, description, user, date)); } } }
//------------------------------------------------------------------------- public static void GetChangelistsFromP4( DateTime fromDate, DateTime toDate, ref List <Changelist> changelists) { string output = Perforce.RunCommand( "changes -s submitted -t -l @" + fromDate.ToString("yyyy/MM/dd:HH:mm:ss") + ",@" + toDate.ToString("yyyy/MM/dd:HH:mm:ss")); string[] lines = output.Split(new string[] { Environment.NewLine }, StringSplitOptions.None); for (int lineIndex = 0; lineIndex < lines.Length; lineIndex++) { string line = lines[lineIndex]; // Extract the changelist number. if (line.Length < "Change ".Length) { continue; } string tmp = line.Remove(0, "Change ".Length); int index = tmp.IndexOf(' '); if (index < 0) { continue; } string changelistNumberStr = tmp.Substring(0, index); int changelistNumber; if (int.TryParse(changelistNumberStr, out changelistNumber) == false) { continue; } // Exract the date & time. tmp = tmp.Remove(0, (changelistNumberStr + " on ").Length); string dateStr = tmp.Substring(0, 19); DateTime date = new DateTime(); if (DateTime.TryParse(dateStr, out date) == false) { continue; } // Extract the user. tmp = tmp.Remove(0, (dateStr + " by ").Length); string username = tmp.Substring(0, tmp.IndexOf('@')); // Extract the description. string description = ""; if (lineIndex + 2 < lines.Length) { lineIndex += 2; while (lineIndex < lines.Length) { if (lines[lineIndex].IndexOf("Change") == 0) { lineIndex--; break; } description += lines[lineIndex++]; } } // Create the changelist object if we don't already have one. if (Changelists.ContainsKey(changelistNumber) == false) { changelists.Add( new Changelist( changelistNumber, description, username, date)); } } }
//------------------------------------------------------------------------- public static void GetChangelistFilesFromP4( int changelistId, out List <ChangelistFile> files) { files = new List <ChangelistFile>(); //-- Get changelist's files from P4. string output = Perforce.RunCommand("describe -ds " + changelistId); // Exract files from output. int index = output.IndexOf("Affected files ..."); if (index > -1) { while ((index = output.IndexOf("... ", index + 1)) > -1) { // Skip the "... " prefixing the path. index += 4; // Grab the path. int revisionIndex = output.IndexOf('#', index); if (revisionIndex < 0) { Program.Log.AddEntry( Log.EntryType.ERROR, "Failed to find revision index in file path.", true); continue; } int revisionEndIndex = output.IndexOf(' ', revisionIndex); if (revisionIndex < 0) { Program.Log.AddEntry( Log.EntryType.ERROR, "Failed to find revision END index in file path.", true); continue; } string path = output.Substring(index, revisionEndIndex - index); // Add file path to the ui list. files.Add(new ChangelistFile(path)); } } // Go through the differences section and extract the various counts. index = output.IndexOf("Differences ..."); if (index > -1) { string filename = ""; int endIndex = -1; int addCount = 0; int deletedCount = 0; int changedCount = 0; while ((index = output.IndexOf("==== ", index)) > -1) { // Extract the filename. index += "==== ".Length; endIndex = output.IndexOf(" ", index); filename = output.Substring(index, endIndex - index); // 'Add' count. index = output.IndexOf("add ", index); if (index < 0) { break; } index += "add ".Length; endIndex = output.IndexOf("chunks", index); if (int.TryParse( output.Substring( index, endIndex - index), out addCount) == false) { continue; } // 'Deleted' count. index = output.IndexOf("deleted ", index); index += "deleted ".Length; endIndex = output.IndexOf("chunks", index); if (int.TryParse( output.Substring( index, endIndex - index), out deletedCount) == false) { continue; } // 'Changed' count. index = output.IndexOf("changed ", index); index += "changed ".Length; endIndex = output.IndexOf("chunks", index); if (int.TryParse( output.Substring( index, endIndex - index), out changedCount) == false) { continue; } // Update the file with its stats. foreach (ChangelistFile file in files) { if (file.Filename.Equals(filename, StringComparison.OrdinalIgnoreCase)) { file.AdditionsCount = addCount; file.DeletionsCount = deletedCount; file.ChangesCount = changedCount; } } } } }