/// <summary> /// Start scanning at the time the form is first shown /// </summary> private void FormNewRepoScanAddShown(object sender, EventArgs e) { enableAdd = true; int count = 1; // Create each of the repos for the selected directories foreach (var d in dirs) { if (enableAdd == false) { return; } try { // Update progress bar and make sure it gets painted progressBar.Value = count++; Thread.Sleep(1); textRepo.Text = d; Application.DoEvents(); Directory.SetCurrentDirectory(d); if (ClassGit.Run("init").Success() == false) { throw new ClassException("init failed."); } App.Repos.Add(d); } catch (Exception ex) { App.PrintLogMessage("Unable to add repo: " + ex.Message, MessageType.Error); App.PrintStatusMessage(ex.Message, MessageType.Error); } } DialogResult = DialogResult.OK; }
/// <summary> /// This function is run on a separate thread. It checks for a new version. /// </summary> private void AltThreadVersionCheck() { // A lot of things can go wrong here... try { StringBuilder file = new StringBuilder(); using (WebResponse response = altRequest.GetResponse()) { using (StreamReader reader = new StreamReader(response.GetResponseStream())) { file.Append(reader.ReadToEnd()); } } // TODO... if (file.ToString().Length > 0) { Thread.Sleep(2000); // Make sure this message is shown after all other messages in the view pane App.PrintStatusMessage(file.ToString(), MessageType.NewVersion); } } catch (Exception ex) { App.PrintStatusMessage("Version check: " + ex.Message, MessageType.Error); } }
/// <summary> /// Load .netrc file into internal data structure /// </summary> private void LoadNetrc(string filename) { try { using (StreamReader file = new StreamReader(filename)) { do { string line = file.ReadLine(); if (line == null) { break; } if (line == string.Empty) { continue; } List <string> keys = line.Split((" ").ToCharArray(), StringSplitOptions.RemoveEmptyEntries).ToList(); if ((keys.Count() != 6) || (keys[0] != "machine") || (keys[2] != "login") || (keys[4] != "password")) { throw new Exception("Bad entry: " + line); } netrc[keys[1]] = new Tuple <string, string>(keys[3], keys[5]); // [machine] = (login, password) } while (true); } } catch (Exception ex) { App.PrintStatusMessage(ex.Message, MessageType.Error); MessageBox.Show(ex.Message, "Error reading .netrc file", MessageBoxButtons.OK, MessageBoxIcon.Error); } }
/// <summary> /// Open a file browser/Explorer at the specific directory, optionally selecting a file /// </summary> public static void ExplorerHere(string where, string selFile) { try { App.PrintStatusMessage("Opening a file browser at " + where, MessageType.General); // WAR: Opening an "Explorer" is platform-specific if (IsMono()) { // TODO: Start a Linux (Ubuntu?) file explorer in a more flexible way Process.Start(@"/usr/bin/nautilus", "--browser " + where); } else { string path = selFile == string.Empty ? "/e,\"" + where + "\"" : "/e, /select,\"" + selFile + "\""; App.PrintLogMessage(path, MessageType.Command); Process.Start("explorer.exe", path); } } catch (Exception ex) { App.PrintLogMessage(ex.Message, MessageType.Error); MessageBox.Show(ex.Message, "Explorer Here error", MessageBoxButtons.OK, MessageBoxIcon.Error); } }
/// <summary> /// Add a new repository with the root at the given path. Create a directory if it does not exist. /// This function throws exceptions! /// </summary> /// <param name="root">The root path of a new repository</param> /// <returns>Newly created repository class or null if a repo already exists at that root directory</returns> public ClassRepo Add(string root) { // Detect a repository with the same root path (case insensitive directory name compare) if (Repos.Exists(r => r.Path.Equals(root, StringComparison.CurrentCultureIgnoreCase))) { throw new ClassException("Repository with the same name already exists!"); } Directory.CreateDirectory(root); ClassRepo repo = new ClassRepo(root); if (!repo.Init()) { throw new ClassException("Unable to initialize git repository!"); } Repos.Add(repo); App.PrintStatusMessage("Adding repo " + repo, MessageType.General); // If this is a very first repo, set it as default and current if (Repos.Count == 1) { Current = Default = repo; } return(repo); }
/// <summary> /// Commit a list of files. /// Returns true if commit succeeded, false otherwise. /// </summary> public bool GitCommit(string cmd, bool isAmend, List <string> files) { ExecResult result; string list = QuoteAndFlattenPaths(files); App.PrintStatusMessage("Submit " + list, MessageType.General); // See below Run() for the description of the problem with long commands. // The Run() function breaks any command into chunks of 2000 characters or less // and issues them separately. This can be done on every command except 'commit' // since that would introduce multiple commits, which is probably not what the user // wants. Hence, we break commit at this level into an initial commit of a single // file (the first file on the list), and append for each successive chunk. if (isAmend == false && list.Length >= 2000) { result = RunCmd("commit " + cmd + " -- " + "\"" + files[0] + "\""); if (result.Success() == false) { return(false); } isAmend = true; files.RemoveAt(0); list = QuoteAndFlattenPaths(files); } result = RunCmd(string.Format("commit {0} {1} -- {2}", cmd, isAmend ? "--amend" : "", list)); return(result.Success()); }
/// <summary> /// Reset a list of files to a specific head. /// Returns true if a git command succeeded, false otherwise. /// </summary> public bool GitReset(string head, List <string> files) { string list = QuoteAndFlattenPaths(files); App.PrintStatusMessage(string.Format("Resetting to {0}: {1}", head, list), MessageType.General); return(RunCmd("reset " + head + " -- " + list).Success()); }
/// <summary> /// Callback on the command line text ready. /// We execute a custom (immediate) command which can be either a direct git /// command or a shell (command prompt?) command. /// Several commands may be separated by "&&" token. This accomodates Gerrit /// code review process and its shortcuts that can be easily pasted. /// </summary> private void CmdBoxTextReady(object sender, string cmd) { foreach (string command in cmd.Split(new[] { " && " }, StringSplitOptions.RemoveEmptyEntries)) { // Print out the command itself App.PrintStatusMessage(command, MessageType.Command); // If the command text started with a command 'git', remove it string[] tokens = command.Split(' '); string args = String.Join(" ", tokens, 1, tokens.Count() - 1); // We are guaranteed to have at least one token (by the TextBoxEx control) string run; if (tokens[0].ToLower() == "git") { // Command is a git command: execute it run = ClassGit.Run(args).ToString(); } else { // Command is an arbitrary (command line type) command // Use the command shell to execute it run = ClassUtils.ExecuteShellCommand(tokens[0], args); } App.PrintStatusMessage(run, MessageType.Output); } }
/// <summary> /// Checkout a list of files /// </summary> public void GitCheckout(string options, List <string> files) { string list = QuoteAndFlattenPaths(files); App.PrintStatusMessage("Checkout " + options + " " + list, MessageType.General); RunCmd("checkout " + options + " -- " + list); }
/// <summary> /// Revert a list of files /// </summary> public void GitRevert(List <string> files) { string list = QuoteAndFlattenPaths(files); App.PrintStatusMessage("Reverting " + list, MessageType.General); RunCmd("checkout -- " + list); }
public void GitDelete(string tag, List <string> files) { string list = QuoteAndFlattenPaths(files); App.PrintStatusMessage("Removing " + list, MessageType.General); RunCmd("rm " + tag + " -- " + list); }
public void GitDiff(string tag, List <string> files) { string list = QuoteAndFlattenPaths(files); App.PrintStatusMessage("Diffing " + list, MessageType.General); RunCmd("difftool " + ClassDiff.GetDiffCmd() + " " + tag + " -- " + list, true); }
public static ExecResult Run(string gitcmd, bool async) { // Pick up git commands that take long time to execute and run them // using a threaded execution if (gitcmd.StartsWith("clone --progress") || gitcmd.StartsWith("fetch") || gitcmd.StartsWith("pull ") || gitcmd.StartsWith("push ")) { FormGitRun formGitRun = new FormGitRun(Properties.Settings.Default.GitPath, gitcmd); formGitRun.ShowDialog(); return(formGitRun.GetResult()); } if (!async) { return(Exec.Run(Properties.Settings.Default.GitPath, gitcmd)); } var job = new Exec(Properties.Settings.Default.GitPath, gitcmd); job.AsyncRun(s => App.PrintStatusMessage(s, MessageType.Output), s => App.PrintStatusMessage(s, MessageType.Error), null); return(new ExecResult()); }
/// <summary> /// Open a command prompt at the specific directory /// </summary> public static void CommandPromptHere(string where) { try { Directory.SetCurrentDirectory(where); App.PrintStatusMessage("Command prompt at " + where, MessageType.General); Process proc = new Process(); proc.StartInfo.UseShellExecute = false; // Add all environment variables listed foreach (var envar in Env) { proc.StartInfo.EnvironmentVariables.Add(envar.Key, envar.Value); } // WAR: Opening a command window/terminal is platform-specific if (IsMono()) { // TODO: This may not work on a non-Ubuntu system? proc.StartInfo.FileName = @"/usr/bin/gnome-terminal"; proc.StartInfo.Arguments = "--working-directory=" + where; } else { proc.StartInfo.FileName = "cmd.exe"; } proc.Start(); } catch (Exception ex) { MessageBox.Show(ex.Message, "Command Prompt Here error", MessageBoxButtons.OK, MessageBoxIcon.Error); } }
/// <summary> /// Callback that handles process printing to stderr /// Print to the application status pane. /// </summary> private void ProcErrorDataReceived(object sender, DataReceivedEventArgs e) { if (String.IsNullOrEmpty(e.Data)) { return; } App.PrintStatusMessage(e.Data + Environment.NewLine, MessageType.Error); }
/// <summary> /// Rename a list of files /// </summary> public void GitRename(List <string> files) { string list = QuoteAndFlattenPaths(files); App.PrintStatusMessage("Renaming " + list, MessageType.General); RunCmd("add -- " + list); // Any git command that adds/updates files in the index might cause file check for TABs ClassTabCheck.CheckForTabs(files); }
/// <summary> /// Parses the response string from new version check sites /// </summary> private void ParseNewVersionResponse(StringBuilder answer) { if (MessageAlreadySent) // Print only one message for multiple site's checks { return; } // Search for the version string, for example: // [assembly: AssemblyFileVersion("1.0.11")] string sPattern = @"GitForce-(?<major>\d+).(?<minor>\d+).(?<build>\d+).exe"; Regex r = new Regex(sPattern, RegexOptions.Compiled); if (r.IsMatch(answer.ToString())) { // Get the version numbers from the latest version checked in github string major = r.Match(answer.ToString()).Result("${major}"); string minor = r.Match(answer.ToString()).Result("${minor}"); string build = r.Match(answer.ToString()).Result("${build}"); int webMajor = Convert.ToInt32(major); int webMinor = Convert.ToInt32(minor); int webBuild = Convert.ToInt32(build); // Get the current version numbers string[] current = GetVersion().Split('.'); int thisMajor = Convert.ToInt32(current[0]); int thisMinor = Convert.ToInt32(current[1]); int thisBuild = Convert.ToInt32(current[2]); // Compare two versions and set flag if the current one is less if (thisMajor < webMajor) { NewVersionAvailable = true; } else if (thisMinor < webMinor) { NewVersionAvailable = true; } else if (thisBuild < webBuild) { NewVersionAvailable = true; } // By now we have log window availabe, so print out what's going on if (NewVersionAvailable) { App.PrintStatusMessage("**** A new version of GitForce is available! ****", MessageType.NewVersion); } else { App.PrintStatusMessage("Version check OK - this version is up-to-date.", MessageType.General); } MessageAlreadySent = true; } else { App.PrintStatusMessage("Version check: Unable to match pattern!", MessageType.Error); } }
/// <summary> /// Export current set of custom tools to a file /// </summary> private void ExportToolMenuItemClick(object sender, EventArgs e) { if (saveTools.ShowDialog() == DialogResult.OK) { if (App.CustomTools.Save(saveTools.FileName)) { App.PrintStatusMessage("Saved custom tools to " + saveTools.FileName, MessageType.General); } } }
/// <summary> /// Open a file browser/Explorer at the specific directory, optionally selecting a file /// </summary> public static void ExplorerHere(string where, string selFile) { try { App.PrintStatusMessage("Opening a file browser at " + where, MessageType.General); if (IsMono()) { // Opening an "Explorer" is platform-specific and depends on the desktop environment which you use, // each desktop environment comes with its own default file- manager. // Ubuntu: nautilus // Cinnamon: nemo // Mate: caja // xfce: thunar // KDE: dolphin if (File.Exists(@"/usr/bin/nautilus")) { Process.Start(@"/usr/bin/nautilus", "--browser " + where); } else if (File.Exists(@"/usr/bin/nemo")) { Process.Start(@"/usr/bin/nemo", where); } else if (File.Exists(@"/usr/bin/caja")) { Process.Start(@"/usr/bin/caja", where); } else if (File.Exists(@"/usr/bin/thunar")) { Process.Start(@"/usr/bin/thunar", where); } else if (File.Exists(@"/usr/bin/dolphin")) { Process.Start(@"/usr/bin/dolphin", where); } else { throw new Exception("Unable to identify a file browser program for this distro. Please report this issue - thank you!"); } } else { string path = selFile == string.Empty ? "/e,\"" + where + "\"" : "/e, /select,\"" + selFile + "\""; App.PrintLogMessage(path, MessageType.Command); Process.Start("explorer.exe", path); } } catch (Exception ex) { App.PrintLogMessage(ex.Message, MessageType.Error); MessageBox.Show(ex.Message, "Explorer Here error", MessageBoxButtons.OK, MessageBoxIcon.Error); } }
/// <summary> /// Given the topic, open the relevant help page online /// </summary> public static void Handler(string topic) { if (Webhelp.ContainsKey(topic)) { Process.Start(Webhelp[topic]); } else { App.PrintStatusMessage("Internal Error: Please report that `topic " + topic + "` not found!", MessageType.Error); } }
/// <summary> /// Run the external diff command on a given file or a list of files /// </summary> public void GitDiff(string tag, List <string> files) { string list = QuoteAndFlattenPaths(files); if (list == "\"\"") // For now, we don't want to match all paths but only diff selected files { App.PrintStatusMessage("Diffing: No files selected and we don't want to match all paths.", MessageType.General); return; } App.PrintStatusMessage("Diffing " + list, MessageType.General); RunCmd("difftool " + ClassDiff.GetDiffCmd() + " " + tag + " -- " + list, true); }
/// <summary> /// Import a set of custom tools from a file /// </summary> private void ImportToolMenuItemClick(object sender, EventArgs e) { if (openTools.ShowDialog() == DialogResult.OK) { ClassCustomTools newTools = ClassCustomTools.Load(openTools.FileName); if (newTools != null) { App.CustomTools = newTools; App.PrintStatusMessage("Loaded custom tools from " + openTools.FileName, MessageType.General); } } }
/// <summary> /// Loads username/email for all repos in this workspace. /// Removes repos that are not valid. /// </summary> public void InitAll() { // Get the user name and email for each repo adding invalid ones to the list List <ClassRepo> invalidRepos = Repos.Where(r => r.Init() == false).ToList(); // Remove all invalid repos from the workspace foreach (var r in invalidRepos) { App.PrintStatusMessage("Removing invalid repo " + r, MessageType.General); Delete(r); } }
/// <summary> /// Load workspace given the workspace file name. /// </summary> public static bool Load(string name) { App.PrintStatusMessage("Loading workspace: " + name, MessageType.General); if (App.Repos.Load(name, false)) // Load operation (not merge) { AddLRU(name); Properties.Settings.Default.WorkspaceFile = name; return(true); } App.PrintStatusMessage("Load cancelled. Current workspace file: " + Properties.Settings.Default.WorkspaceFile, MessageType.General); return(false); }
/// <summary> /// Open a command prompt at the specific directory /// </summary> public static void CommandPromptHere(string where) { try { Directory.SetCurrentDirectory(where); App.PrintStatusMessage("Command prompt at " + where, MessageType.General); Process proc = new Process(); proc.StartInfo.UseShellExecute = false; // Add all environment variables listed foreach (var envar in Env) { proc.StartInfo.EnvironmentVariables.Add(envar.Key, envar.Value); } if (IsMono()) { // Opening an command terminal is platform-specific and depends on the desktop environment which you use, // and which terminal application is installed if (File.Exists(@"/usr/bin/gnome-terminal")) { proc.StartInfo.FileName = @"/usr/bin/gnome-terminal"; proc.StartInfo.Arguments = "--working-directory=" + where; } else if (File.Exists(@"/usr/bin/konsole")) { proc.StartInfo.FileName = @"/usr/bin/konsole"; proc.StartInfo.Arguments = "--workdir " + where; } else if (File.Exists(@"/usr/bin/xterm")) { proc.StartInfo.FileName = @"/usr/bin/xterm"; proc.StartInfo.Arguments = "-e \"cd " + where + "; bash\""; } else { throw new Exception("Unable to identify a terminal shell program for this distro. Please report this issue - thank you!"); } } else { proc.StartInfo.FileName = "cmd.exe"; } proc.Start(); } catch (Exception ex) { MessageBox.Show(ex.Message, "Command Prompt Here error", MessageBoxButtons.OK, MessageBoxIcon.Error); } }
/// <summary> /// Executes a shell command /// </summary> public static string ExecuteShellCommand(string cmd, string args) { App.PrintStatusMessage("Shell execute: " + cmd + " " + args, MessageType.Command); // WAR: Shell execute is platform-specific if (IsMono()) { return(Exec.Run(cmd, args).ToString()); } else { args = "/c " + cmd + " " + args; return(Exec.Run("cmd.exe", args).ToString()); } }
/// <summary> /// Import workspace given the file name. /// If the file name is null, return false. /// </summary> public static bool Import(string name) { if (name == null) { return(false); } App.PrintStatusMessage("Importing workspace: " + name, MessageType.General); if (App.Repos.Load(name, true)) // Merge loaded repos with the current set { AddLRU(name); return(true); } App.PrintStatusMessage("Import cancelled. Current workspace file: " + Properties.Settings.Default.WorkspaceFile, MessageType.General); return(false); }
/// <summary> /// Save workspace given the file name. /// If the file name is null, save the current workspace. /// </summary> public static bool Save(string name) { if (name == null) { name = Properties.Settings.Default.WorkspaceFile; } App.PrintStatusMessage("Saving workspace: " + name, MessageType.General); if (App.Repos.Save(name)) { AddLRU(name); Properties.Settings.Default.WorkspaceFile = name; return(true); } return(false); }
/// <summary> /// Save current set of tools to a given file. /// Returns true if save successful. /// If save failed, return false and print the error message to a main pane. /// </summary> public bool Save(string name) { try { XmlSerializer serializer = new XmlSerializer(typeof(ClassCustomTools)); using (TextWriter textWriter = new StreamWriter(name)) { serializer.Serialize(textWriter, this); } } catch (Exception ex) { App.PrintStatusMessage("Error saving custom tools: " + ex.Message, MessageType.Error); return(false); } return(true); }
/// <summary> /// Write back internal data into .netrc file /// </summary> private void SaveNetrc(string filename) { try { using (StreamWriter file = new StreamWriter(filename)) { foreach (var machine in netrc) { file.WriteLine(string.Format("machine {0} login {1} password {2}", machine.Key, machine.Value.Item1, machine.Value.Item2)); } } } catch (Exception ex) { App.PrintStatusMessage(ex.Message, MessageType.Error); MessageBox.Show(ex.Message, "Error writing .netrc file", MessageBoxButtons.OK, MessageBoxIcon.Error); } }