When setting up a merge, the TortoiseSvn client has a wonderful checkbox labeled "Hide non-mergable revisions". I'm looking to reproduce the list of revisions that shows up when it's enabled using SharpSvn.
The TortoiseSvn documentation explains this checkbox:
When merge tracking is used, the log dialog will show previously merged revisions, and revisions pre-dating the common ancestor point, i.e. before the branch was copied, as greyed out. The Hide non-mergeable revisions checkbox allows you to filter out these revisions completely so you see only the revisions which can be merged.
How can I reproduce this functionality in SharpSvn code? I need a list of SvnLogEventArgs (or similar) that are candidates for merging.
Current status: I've only gotten as far as pulling the logs for both branches. I can't figure out how to get the appropriate svn:mergeinfo attribute or what to do with it once I get it.
I kept plugging away, and following links, and here's what I ended up with:
using (var client = new SvnClient())
{
var release = SvnTarget.FromUri(new Uri(#"https://******/branches/Release"));
var trunk = SvnTarget.FromUri(new Uri(#"https://******/trunk"));
string trunkMergeinfo, releaseMergeinfo;
client.GetProperty(release, "svn:mergeinfo", out releaseMergeinfo);
client.GetProperty(trunk, "svn:mergeinfo", out trunkMergeinfo);
var relInfos = releaseMergeinfo.Split("\n");
var trunkInfos = trunkMergeinfo.Split("\n");
// This is here because I don't know what will happen once I merge something into trunk.
Debug.Assert(relInfos.Except(trunkInfos).Count() == 1,"Too many unknown merge paths");
var trunklist = relInfos.SingleOrDefault(i => i.StartsWith("/trunk:"));
var revisions = trunklist.Replace("/trunk:", "").Split(",").SelectMany(t =>
{
// If the log contains a range, break it out to it's specific revisions.
if (t.Contains("-"))
{
var match = Regex.Match(t, #"(\d+)-(\d+)");
var start = int.Parse(match.Groups[1].Value);
var end = int.Parse(match.Groups[2].Value);
return Enumerable.Range(start, end - start + 1).ToArray();
}
else
return new[] { int.Parse(t) };
}).Select(x => (long)x);
Collection<SvnLogEventArgs> baseRevs;
// Why can't this take "trunk" or a property thereof as an argument?
client.GetLog(new Uri(#"https://******/trunk"), new SvnLogArgs { Start = 1725, End = SvnRevisionType.Head }, out baseRevs);
baseRevs.Reverse().Where(r => !revisions.Contains(r.Revision) ).Select(x => x.LogMessage).Dump();
}
Hopefully, this helps someone else, although I'll note that it does not have a lot of the sanity checking that I'd put in production code - this is the quick-and-dirty version.
Try SvnClient.ListMergesEligible:
http://sharpsvn.qqn.nl/current/html/M_SharpSvn_SvnClient_ListMergesEligible_1.htm
Edit.
SharpSVN seems bugged for me, so I went for cmd.
Check this out:
private static void mergelhetőVerziókListája()
{
string revíziók = cmd("svn", "mergeinfo --show-revs eligible \".../branches/dev\" \".../trunk\"");
}
private static string cmd(string utasítás, string paraméter)
{
StringBuilder eredmény = new StringBuilder();
Process cmd = new Process()
{
StartInfo = new ProcessStartInfo
{
FileName = utasítás,
Arguments = paraméter,
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
}
};
cmd.Start();
while (!cmd.StandardOutput.EndOfStream)
{
string kimenet = cmd.StandardOutput.ReadLine();
eredmény.AppendLine(kimenet); //...
}
return eredmény.ToString();
}
Related
try
{
string[] SetupFolderKeywords = {"Setup", "Installed"};
DirectoryInfo SearchedDirectory = new DirectoryInfo(Game.SelectedPath);
FileSystemInfo[] filesAndDirs = SearchedDirectory.GetFileSystemInfos($"*{SetupFolderKeywords[0]}*|*{SetupFolderKeywords[1]}*"); // <-- This doesn't work
// FileSystemInfo[] filesAndDirs = SearchedDirectory.GetFileSystemInfos("*" + SetupFolderKeywords[0] + "*"); <-- This Works
foreach (FileSystemInfo foundFile in filesAndDirs)
{
string FullName = foundFile.FullName;
MessageBox.Show(FullName);
}
}
catch (IOException ExpMoveFolder)
{
MessageBox.Show(Convert.ToString(ExpMoveFolder));
}
I'm trying to look for a folder that has either the keyword "Setup" or "Installed" inside the Game.SelectedPath directory. (I used a FolderBrowserDialog to select this folder) and make a MessageBox appear with its path.
When I try to search for a folder that matches one keyword, the MessageBox appears with the path of the folder. It works great, but when I try to search for keyword "Setup" or "Installed" MessageBox doesn't show at all.
No error messages or warnings appear in visual studio and no program exception occurs when I try to look for either one of the keywords instead of just one keyword.
You can't search for multiple patterns with a single call. Your attempt at a Boolean expression is just interpreted as a single pattern and, of course, there are no entries that match that pattern. If you want to match multiple patterns then you have to make multiple calls. One option might be like this:
var folder = new DirectoryInfo(Game.SelectedPath);
var entries = folder.EnumerateFileSystemInfos(patterns[0]);
for (var i = 1; i < patterns.Length; i++)
{
entries = entries.Concat(folder.EnumerateFileSystemInfos(patterns[i]));
}
foreach (var entry in entries)
{
// Use entry here.
}
EDIT:
I just created this folder:
I then executed this code:
var patterns = new[] { "123", "789" };
var folder = new DirectoryInfo(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "Test"));
var entries = folder.EnumerateFileSystemInfos($"*{patterns[0]}*");
for (var i = 1; i < patterns.Length; i++)
{
entries = entries.Concat(folder.EnumerateFileSystemInfos($"*{patterns[i]}*"));
}
foreach (var entry in entries)
{
Console.WriteLine(entry.Name);
}
That's basically exactly what I posted above except I added wildcards to the EnumerateFileSystemInfos calls where the original code would have required them to be in strings already. This is the output I got:
File123.txt
Folder123
File789.txt
Folder789
I then changed the filters to this:
var patterns = new[] { "456" };
and ran the code again and got this output:
File456.txt
Folder456
Clearly, the code works exactly as it is supposed to and, if what you did didn't work then you did it wrong. If you can't work out what you did wrong, I suggest that you update your question and add the new relevant information.
So I am a Software Packager and I am working on a little project for my company to automate the packaging of some of the free software available via Ninite and Chocolatey. I have managed to get the Ninite stuff working fine but Chocolatey is proving to be a pain.
This section of the code is supposed to compare the app versions from my .csv to data returned from querying chocolatey via cmd.exe.
If the versions don't match I have some other classes that deal with updating the .csv with the latest available version and the rest of the code creates some config files to be used by the other software I am dealing with. This bit is irrelevant as I have managed to get it to work. It's simply just the fact that for some unknown reason the strings returned from querying chocolatey are not behaving as they should.
I initially thought the issue may have been caused by the way I was using .Replace() to filter unwanted data but at this point if I remove all instances of .Replace() and just simply use the data stored in line I get the same results.
The issue seems to lay somewhere in the GetChocApps() method and the print outs are from the CompareVersions() method.
(Sorry if I have provided a tad more code than I should have but I personally thought it was all relevant)
using System;
using System.Diagnostics;
using System.IO;
using System.Collections.Generic;
namespace XXXX_PackagingAutomation
{
class Choc
{
private Dictionary<string, string> chocApps = new Dictionary<string, string>();
private Dictionary<string, string> csvApps = new Dictionary<string, string>();
// For each app a new entry is to be added
// Key: Name in Chocolatey
// Value: Name to appear in CSV
private Dictionary<string, string> chocAppsToCheck = new Dictionary<string, string>() {
{"adobereader", "Adobe_Acrobat_Reader_DC"}
};
private List<Data> csvData; // Used to hold csv data passed in from Main method
#region Run
public void Run(List<Data> csvDataIn)
{
csvData = csvDataIn;
GetChocApps();
CsvAppsToDic(); // Place all the CSV apps along with corresponding versions to csvApps dictionary
CompareVersions();
}
#endregion
#region Get Choc Apps
private void GetChocApps()
{
foreach (var appName in chocAppsToCheck)
{
List<string> chocoTempList = new List<string>();
ProcessStartInfo procStartInfo = new ProcessStartInfo()
{
FileName = "cmd.exe",
Arguments = $"/c choco search {appName.Key} --exact",
RedirectStandardError = true,
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true,
};
//Process.Start(procStartInfo);
Process p = new Process();
p.StartInfo = procStartInfo;
p.Start();
StreamReader outputWriter = p.StandardOutput;
string errorReader = p.StandardError.ReadToEnd();
string lines = outputWriter.ReadToEnd();
string[] individualLines = lines.Split("\n"[0]);
// All lines returned from the command are then added to the chocoTempList
foreach (string line in individualLines)
chocoTempList.Add(line);
p.WaitForExit();
foreach (string line in chocoTempList)
{
// If the line contains the information we are looking for then we add it to the choco dictionaries
if (!line.Contains("Chocolatey v") && !line.Contains("packages found") && line.Contains(appName.Key))
{
#region Adobe Acrobat Reader DC
if (appName.Key == "adobereader")
{
// Filter the line to exclude anything else other than the version which is what we want
// and then assign it to the version variable.
string version = line.Replace($"{appName.Key} ", "").Replace(" [Approved]", "").Replace(".", "-").Replace("/n", "");
// Add the Name and version to the chocoApps dictionary from the chocoTempList
chocApps.Add(appName.Value, version);
}
#endregion
}
}
}
}
#endregion
#region CSV apps to Dictionary
private void CsvAppsToDic()
{
foreach (var app in csvData)
csvApps.Add(app.name, app.version);
}
#endregion
#region Compare Versions
private void CompareVersions()
{
// Run this for each entry in the chocApps Dictionary
foreach (var cApp in chocApps)
{
// If the
foreach (var app in csvApps)
{
// Find the matching name
if (app.Key == cApp.Key)
{
// Check if versions match
if (app.Value == cApp.Value)
{
// If the versions match
//Global.WriteVersion()
Console.WriteLine("Versions match :D");
Console.WriteLine($"{app.Value}a");
Console.WriteLine($"{cApp.Value}a");
}
else
{
// If they don't match
Console.WriteLine("This Version should have matched but it doesn't match :(\nRaw print outs:");
Console.WriteLine($"{cApp.Value}"); // chocoApps Dict
Console.WriteLine($"{app.Value}"); // csvApps Dict
// Heres where the funny bit happens :D
// Note how I have added some characters to the end of the line *abc
Console.WriteLine("\n\n");
Console.WriteLine($"{cApp.Value}*abc"); // chocoApps Dict
Console.WriteLine($"{app.Value}*abc"); // csvApps Dict
}
}
}
}
}
#endregion
}
}
This else statement should not have been called since the two strings should have matched.
Here's a screenshot which includes the console output of the running code.
it might just be case of white space, try:
if (app.Value.Trim() == cApp.Value.Trim())
{
//etc
}
edit: Trim() removes all white space at the start and end of the string including \r and \n also, so will also fix your issue above due to /n i.e. replace .Replace("/n","") with Trim() where you assign the string. Covers a number of issues with simpler code.
I am using libgit2sharp in a c# solution to switch to a branch and pull in the latest changes. Here is the code I am using:
public void FetchAll()
{
using (var repo = new Repository(_LocalGitPath))
{
foreach (Remote remote in repo.Network.Remotes)
{
FetchOptions options = new FetchOptions();
options.CredentialsProvider = new CredentialsHandler((url, usernameFromUrl, types) => new UsernamePasswordCredentials()
{
Username = _UserName,
Password = _Password
});
repo.Network.Fetch(remote, options);
}
}
}
public string CheckoutBranch(string branchName)
{
using (var repo = new Repository(_LocalGitPath))
{
var trackingBranch = repo.Branches[branchName];
if (trackingBranch.IsRemote)
{
branchName = branchName.Replace("origin/", string.Empty);
var branch = repo.CreateBranch(branchName, trackingBranch.Tip);
repo.Branches.Update(branch, b => b.TrackedBranch = trackingBranch.CanonicalName);
Commands.Checkout(repo, branch, new CheckoutOptions { CheckoutModifiers = CheckoutModifiers.Force });
}
else
{
Commands.Checkout(repo, trackingBranch, new CheckoutOptions { CheckoutModifiers = CheckoutModifiers.Force });
}
return branchName;
}
}
public void PullBranch(string branchName)
{
using (var repo = new Repository(_LocalGitPath))
{
PullOptions options = new PullOptions();
options.MergeOptions = new MergeOptions();
options.MergeOptions.FailOnConflict = true;
options.FetchOptions = new FetchOptions();
options.FetchOptions.CredentialsProvider = new CredentialsHandler((url, usernameFromUrl, types) => new UsernamePasswordCredentials()
{
Username = _UserName,
Password = _Password
});
repo.Network.Pull(new Signature(_UserName, _Password, new DateTimeOffset(DateTime.Now)), options);
}
}
I have no problem fetching, and checking out a branch. When I try to pull latest, I get an error saying, 'There is no tracking information for the current branch.' I believe that this means that the local branch doesn't know what the correct remote repository is to pull changes from, but I haven't been able to figure out how to tell libgit2sharp what the remote repo path is. Anyone have any suggestions?
While doing research on this problem I found this: https://github.com/libgit2/libgit2sharp/issues/1235. Essentially, a libgit2sharp dev describes the exact problem I am seeing, but doesn't provide any code for the fix.
One additional note: I will never be merging or pushing any changes back from this repository. I am pulling it for an automated build, so we can ignore or overwrite any local changes. I just need to get the latest code.
SOLUTION:
I have updated the code above with the solution that I got working. You need to be really careful to make sure that when you checkout a branch, you check the isRemote flag on the branch you are checking out is set to true. If you checkout a branch that isn't a remote it will set the remote to '.' in your git config file, and you need to manually fix it. If there isn't a valid remote you will not be able to pull the branch changes.
Do not use the code sample on the libgit2sharp wiki unless they add this check in.
You can setup the tracking brach information on the local branch by using the Refspec:
using (var repo = new Repository("/Users/sushi/code/redux/mono"))
{
var trackingBranch = repo.Branches["remotes/origin/work-btls"];
if (trackingBranch.IsRemote)
{
var branch = repo.CreateBranch("SomeLocalBranchName", trackingBranch.Tip);
repo.Branches.Update(branch, b => b.TrackedBranch = trackingBranch.CanonicalName);
repo.Checkout(branch, new CheckoutOptions { CheckoutModifiers = CheckoutModifiers.Force });
}
}
You can use git to verify that SomeLocalBranchName is now tracking remotes/origin/work-btls:
>>>git for-each-ref --format='%(refname:short) <- %(upstream:short)' refs/heads
SomeLocalBranchName <- remotes/origin/work-btls
master <- origin/master
>>>git status
On branch SomeLocalBranchName
Your branch is up-to-date with 'remotes/origin/work-btls'.
I would like to read, modify and write back csproj files.
I've found this code, but unfortunately Engine class is depreciated.
Engine engine = new Engine()
Project project = new Project(engine);
project.Load("myproject.csproj");
project.SetProperty("SignAssembly", "true");
project.Save("myproject.csproj");
So I've continued based on the hint I should use Evaluation.ProjectCollection instead of Engine:
var collection = new ProjectCollection();
collection.DefaultToolsVersion = "4.0";
var project = new Project(collection);
// project.Load("myproject.csproj") There is NO Load method :-(
project.FullPath = "myproject.csproj"; // Instead of load? Does nothing...
// ... modify the project
project.Save(); // Interestingly there is a Save() method
There is no Load method anymore. I've tried to set the property FullPath, but the project still seems empty. Missed I something?
(Please note I do know that the .csproj file is a standard XML file with XSD schema and I know that we could read/write it by using XDocument or XmlDocument. That's a backup plan. Just seeing the .Save() method on the Project class I think I missed something if I can not load an existing .csproj. thx)
I've actually found the answer, hopefully will help others:
Instead of creating a new Project(...) and trying to .Load(...) it, we should use a factory method of the ProjectCollection class.
// Instead of:
// var project = new Project(collection);
// project.FullPath = "myproject.csproj"; // Instead of load? Does nothing...
// use this:
var project = collection.LoadProject("myproject.csproj")
Since i can't comment:
This won't work in .net core without first setting the MSBuild.exe path variable. The code to do so can be found here
https://blog.rsuter.com/missing-sdk-when-using-the-microsoft-build-package-in-net-core/
and is written here
private static void SetMsBuildExePath()
{
try
{
var startInfo = new ProcessStartInfo("dotnet", "--list-sdks")
{
RedirectStandardOutput = true
};
var process = Process.Start(startInfo);
process.WaitForExit(1000);
var output = process.StandardOutput.ReadToEnd();
var sdkPaths = Regex.Matches(output, "([0-9]+.[0-9]+.[0-9]+) \\[(.*)\\]")
.OfType<Match>()
.Select(m => System.IO.Path.Combine(m.Groups[2].Value, m.Groups[1].Value, "MSBuild.dll"));
var sdkPath = sdkPaths.Last();
Environment.SetEnvironmentVariable("MSBUILD_EXE_PATH", sdkPath);
}
catch (Exception exception)
{
Console.Write("Could not set MSBUILD_EXE_PATH: " + exception);
}
}
I'm just getting to grips with the C# API for MSDeploy (Microsoft.Web.Deployment.dll), but I'm struggling to find a way that I can determine the dependencies for a given web server.
Basically, I'd like the C# equivalent of the following MSDeploy command line call:
msdeploy.exe -verb:getDependencies -source:webServer
I've tried the documentation, but I've had no luck. Can anybody point me in the right direction?
Having examined the MSDeploy executable in Reflector, it seems that the getDependencies operation is not exposed by the API (the method is internal).
So instead I've had to fall back on calling out to the command line, and processing the results:
static void Main()
{
var processStartInfo = new ProcessStartInfo("msdeploy.exe")
{
RedirectStandardOutput = true,
Arguments = "-verb:getDependencies -source:webServer -xml",
UseShellExecute = false
};
var process = new Process {StartInfo = processStartInfo};
process.Start();
var outputString = process.StandardOutput.ReadToEnd();
var dependencies = ParseGetDependenciesOutput(outputString);
}
public static GetDependenciesOutput ParseGetDependenciesOutput(string outputString)
{
var doc = XDocument.Parse(outputString);
var dependencyInfo = doc.Descendants().Single(x => x.Name == "dependencyInfo");
var result = new GetDependenciesOutput
{
Dependencies = dependencyInfo.Descendants().Where(descendant => descendant.Name == "dependency"),
AppPoolsInUse = dependencyInfo.Descendants().Where(descendant => descendant.Name == "apppoolInUse"),
NativeModules = dependencyInfo.Descendants().Where(descendant => descendant.Name == "nativeModule"),
ManagedTypes = dependencyInfo.Descendants().Where(descendant => descendant.Name == "managedType")
};
return result;
}
public class GetDependenciesOutput
{
public IEnumerable<XElement> Dependencies;
public IEnumerable<XElement> AppPoolsInUse;
public IEnumerable<XElement> NativeModules;
public IEnumerable<XElement> ManagedTypes;
}
Hopefully this is useful to anybody else that ever tries to do the same thing!
There actually is a way of getting there via the public API by using DeploymentObject.Invoke(string methodName, params object[] parameters).
When "getDependencies" is used for methodName, the method returns an XPathNavigator object:
DeploymentObject deplObj = DeploymentManager.CreateObject(DeploymentWellKnownProvider.WebServer, String.Empty);
var result = deplObj.Invoke("getDependencies") as XPathNavigator;
var xml = XDocument.Parse(result.InnerXml);