Programmatically do "Git blame -w" in C# - c#

I need to programmatically get the last author of a specific line in the Git history with C#.
I tried using libgit2sharp :
var repo = new LibGit2Sharp.Repository(gitRepositoryPath);
string relativePath = MakeRelativeSimple(filename);
var blameHunks = repo.Blame(relativePath);
// next : find the hunk which overlap the desired line number
But this is the equivalent of the command
git blame <file>
And in fact I need
git blame -w <file> (to ignore whitespace when comparing)
Libgit2sharp do not set the -w switch and don't provide any parameter/option to set it.
What are my options ? Do you know any other library compatible with the -w switch of the blame command ?

When I hit similar advanced scenarios where the git lib isn't cutting it, I just shell out using start process to the real git command line. It's not sexy, but it's mighty effective.

Maybe using NGIT library will help. That is direct (automatic) port of java JGIT library. Install via nuget package, then:
static void Main() {
var git = Git.Init().SetDirectory("C:\\MyGitRepo").Call();
string relativePath = "MyFolder/MyFile.cs";
var blameHunks = git.Blame().SetFilePath(relativePath).SetTextComparator(RawTextComparator.WS_IGNORE_ALL).Call();
blameHunks.ComputeAll();
var firstLineCommit = blameHunks.GetSourceCommit(0);
// next : find the hunk which overlap the desired line number
Console.ReadKey();
}
Note SetTextComparator(RawTextComparator.WS_IGNORE_ALL) part.

Unfortunately, libgit2sharp is too slow on extracting blames and using this feature is impractical in real scenarios. So, the best way I think is to employ a Powershell script to use the underlying superfast native git. And then redirect the result to your application.
git blame -l -e -c {commit-sha} -- "{file-path}" | where { $_ -match '(?<sha>\w{40})\s+\(<(?<email>[\w\.\-]+#[\w\-]+\.\w{2,3})>\s+(?<datetime>\d\d\d\d-\d\d-\d\d\s\d\d\:\d\d:\d\d\s-\d\d\d\d)\s+(?<lineNumber>\d+)\)\w*' } |
foreach { new-object PSObject –prop #{ Email = $matches['email'];lineNumber = $matches['lineNumber'];dateTime = $matches['dateTime'];Sha = $matches['sha']}}

Related

C# Should I use CertUtil to compute hash of a zip file

Answer might be a bit opinion-based but really important to me as I am pretty sure that certutil is accurate. Not sure for c# MD5 class.
I have a zip file and to verify if it's correct, I want to find its MD5 hash value. This is to then extract the zip file and use its contents in my C# .Net Framework 4.8 console application.
I have currently asked clients (each client has a my men appointed for tech support) to use CertUtil -hashfile command to get the hash and verify it but now, I guess due to increase in clients, I must automate it in my app and give a relief to my men.
I am confused should I use CertUtil and get the output in a C# string using Process.Start() or should i use the .net framework's MD5 class.
C# app is deployed only on windows 10 and I have administrative access to it so not finding certutil isn't an excuse.
Using CertUtil it will be something like this:
public static bool check_correct_installation()
{
var md5Checksum = "";
var startInfo = new ProcessStartInfo
{
WindowStyle = ProcessWindowStyle.Hidden,
FileName = "cmd.exe",
Arguments = $"/C CertUtil -hashfile \"{HolocronFolders["Root"]}\" MD5 | find /i /v \"md5\" | find /i /v \"certutil\"",
};
using var process = new Process {StartInfo = startInfo};
process.OutputDataReceived += (sender, e) => md5Checksum = e.Data;
process.Start();
var fileToRead = $"{HolocronFolders["Council"]}\\force.sith";
if (!File.Exists(fileToRead)) return false;
var sithForce = JsonSerializer.Deserialize<SithForce>(File.ReadAllText(fileToRead));
return sithForce != null && sithForce.Checksum.Md5.ToString() != md5Checksum.Trim();
}
Yes, opinion based, but still here is mine:
Well, you're launching 4(!) processes for each signature you create (cmd.exe, certutil.exe and 2x find.exe). That alone would drag me away from it.
Then, the MD5 classes are being used in a multitude of applications projects, I would say there is no objective way to distrust them, unless you have a proven example where they were "wrong" or an security advisory, etc. that says so.
Finally, the MD5 implementation uses the underlying Windows API (see here to look into the rabbit whole) anyway. So chances are that it uses the same code (in the end) aus CertUtil.exe.
As you said, this might be an opinion-based answer, however I don't see many issues with sticking to your use as you stated with the code. Searching on the internet, it seems that other people chose similar approach, too. Give it a try!

How to verify if rabbitmq is installed on windows?

Is there a way to find out if rabbitmq is installed on the machine?
Either a command line or powershell script or something in C# because I am trying to check it in my c# code.
I searched for it and found only this one, but it did not help much for my case
Verify version of rabbitmq
EDIT
Just found this code snippet in one of the answers of the above post, but not sure it is the right way
public string GetRabbitMqVersion()
{
string prefix = "rabbitmq_server-";
var dirs = System.IO.Directory.EnumerateDirectories(#"C:\Program Files (x86)\RabbitMQ Server", string.Format("{0}*",prefix));
foreach (var dir in dirs)
{
//Just grab the text after 'rabbitmq_server-' and return the first item found
var i = dir.LastIndexOf(prefix);
return dir.Substring(i+16);
}
return "Unknown";
}
As the documentation says you should have a directory with the file rabbitmqctl.bat
The file should be placed on C:\Program Files\RabbitMQ\rabbitmq_server-x.x.x\sbin\
Then you can run any command in a cmd like this rabbitmq-service.bat status
https://www.rabbitmq.com/install-windows-manual.html
A list of installed software is available. What is the name of the RabbitMQ app in this list? Once this is known, it is easy to identify.
powershell -NoLogo -NoProfile -Command ^
"Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* |" ^
"Select-Object DisplayName, DisplayVersion, Publisher, InstallDate |" ^
"Sort-Object -Property DisplayName"

Programatically Pin\UnPin the folder from quick access menu in windows 10

I have a desktop application written in c#, and this application enables users to create the folder on their machine Hard drive . on windows 7 and 8, The App creates a shortcut for this folder under Favorit menu on the left side of windows Explorer window.
In windows 10 there is no Favorite menu, it was replaced by Quick access menu, and if you right click on the folder you can choose to Pin folder for quick access.
To do this programmatically from inside c# code, I found a .exe that can execute the Pin action as if the user clicked on the menu item to pin the folder
I got it from here http://www.maddogsw.com/cmdutils/
The problem is this exe does not contain an option for Unpin the folder from quick access so i will not be able to remove the shortcut from the quick access menu unless if I deleted it and I don't want to do that.
I tried to find the shortcut file and I found it in this path
%AppData%\Windows\Recent\AutomaticDestinations
but there is no mapping between this file shortcut and the file itself. and at the same time when I delete the files from this path, all the Pinned folders shortcut delete from the quick access not only my shortcut.
anyone can help in this ??
Do I need to know if there is any command that I can use it to Pin\Unpin folders to quick access from the command prompt?
I know it's a bit late, but I've found a way to do it and thought maybe someone could still use this.
So as was mentioned by Bradley Uffner, there is no API for this to avoid the constant abuse of such APIs. But there is still a (rather ugly) way to do it!
I'm no expert in PowerShell, but I found a way to do it using PowerShell:
# To add 'C:\path\to\folder' to quick access:
$qa = New-Object -ComObject shell.application
$qa.NameSpace('C:\path\to\folder').Self.InvokeVerb("pintohome")
# To remove 'C:\path\to\folder' from quick access:
($qa.Namespace("shell:::{679F85CB-0220-4080-B29B-5540CC05AAB6}").Items() | Where-Object { $_.Path -EQ 'C:\path\to\folder' }).InvokeVerb("unpinfromhome")
Which finally led me to the solution using C#:
using System.Management.Automation;
using System.Management.Automation.Runspaces
private static void AddFolderToQuickAccess(string pathToFolder)
{
using (var runspace = RunspaceFactory.CreateRunspace())
{
runspace.Open();
var ps = PowerShell.Create();
var shellApplication =
ps.AddCommand("New-Object").AddParameter("ComObject", "shell.application").Invoke();
dynamic nameSpace = shellApplication.FirstOrDefault()?.Methods["NameSpace"].Invoke(pathToFolder);
nameSpace?.Self.InvokeVerb("pintohome");
}
}
private static void RemoveFolderFromQuickAccess(string pathToFolder)
{
using (var runspace = RunspaceFactory.CreateRunspace())
{
runspace.Open();
var ps = PowerShell.Create();
var removeScript =
$"((New-Object -ComObject shell.application).Namespace(\"shell:::{{679f85cb-0220-4080-b29b-5540cc05aab6}}\").Items() | Where-Object {{ $_.Path -EQ \"{pathToFolder}\" }}).InvokeVerb(\"unpinfromhome\")";
ps.AddScript(removeScript);
ps.Invoke();
}
}
NOTE: For this to work, you need to add a reference to System.Management.Automation which can easily be obtained as a nuget.

C# git command line process

Our organization utilizes VisualStudioOnline, GitHub and BitBucket for various repositories. I've been trying to figure out a way in c# to automate the pull of changes from various git repositories on a regular schedule. I've tried starting a process like this and then redirecting the standard input/output.
var p = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = #"C:\Program Files (x86)\Git\bin\sh.exe",
RedirectStandardInput = true,
UseShellExecute = false,
RedirectStandardOutput = true,
Arguments = "--login -i"
}
};
p.Start();
using (var so = p.StandardOutput)
{
using (var si = p.StandardInput)
{
if (si.BaseStream.CanWrite)
{
...
}
}
}
Inside of the block, I'm able to execute git commands like this:
si.WriteLine("cd \"P:/code/testapp\""); which navigates the git
shell to that directory
si.WriteLine("git rev-parse HEAD");
which will give me the latest hash of the repository at that location.
I can see the hash returned in the bash shell and can also read it in through the standard input like this var hash = so.ReadLine();
When I try to do si.WriteLint("git pull"); though, it asks for username and password, which I would expect, but that isn't done through the standard input/output. I can't us var prompt = so.ReadLine(); to get the prompt for username or password and I can't use si.WriteLine("username"); to input my password to the prompt.
I've tried a number of things to get this to work, but so far no luck.
WARNING: messy code ahead, just tried to quickly prototype something, not create a masterpiece
Attempt 1: used standard input/output redirects as shown above to try to write/read the prompts for username/password.
Attempt 2: attempt to get the ssh-agent process that is being utilized by the git bash and write to it
si.WriteLine("ssh-agent --help");
var sshAgentInfo = string.Empty;
var tmp = a.ReadLine();
while (a.Peek() != -1)
{
sshAgentInfo += tmp;
tmp = a.ReadLine();
}
var begPos = sshAgentInfo.IndexOf("SSH_AGENT_PID=", StringComparison.Ordinal);
begPos = begPos + "SSH_AGENT_PID=".Length;
var endPos = sshAgentInfo.IndexOf(";", begPos, StringComparison.Ordinal);
var processId = int.Parse(sshAgentInfo.Substring(begPos, endPos - begPos));
var sshProcess = Process.GetProcessById(processId);
sshProcess.StartInfo.RedirectStandardInput = true;
sshProcess.StartInfo.RedirectStandardInput = true;
using (var si1 = sshProcess.StandardInput) { ... }
Attempt 3: Utilize credential git config credential.helper wincred
This and four were very similar attempts. Again just trying to figure out how to set the password in either of these credential managers from the command line.
Attempt 4: Utilize Git-Credential-Manager-for-Windows
I've tried looking through the documentation here and it seems as though there is an enhancement request to do something along these lines.
Both of these seem to have similar problems to attempt one. The git shell seems to be invoking another process which handles the standard input and output separate from the git shell. The difference and hope I have for these last two though is, Is there a way to call into those credential managers directly to set username/passwords for different urls? In the .git directory, the config file has the credentials setting to tell it which manager to use and it stores the username and email in plain text.
If I invoke a git pull through the shell on the machine and enter my credentials once through that shell it seems to store them, but is there a way to allow users to enter their credentials through a website and then call into the manager through the command line to securely store that information for future automated use?
I also came across this. I haven't had a chance to try it yet, but is this a viable option for doing something like this?
Any help would be greatly appreciated.

How to retrieve git Commit Id from a C# Application?

I am looking into a CI Build Automation Task and I want to name my builds using the Git Commit Id.
I am planning to write a C# program to do this. What Libraries I can use to call Git repositories from C#? I am able to call into local repository clone and retrieve this information using git.exe(Windows) or libgit2sharp but I don't know how to do it on a remote source
From a CI perspective, you may be willing to build a specific branch.
The following piece of code demonstrates this.
using (Repository repo = Repository.Clone(url, localPath))
{
// Retrieve the branch to build
var branchToBuild = repo.Branches["vNext"];
// Updates the content of the working directory with the content of the branch
branchToBuild.Checkout();
// Perform your build magic here ;-)
Build();
// Retrieve the commit sha of the branch that has just been built
string sha = branchToBuild.Tip.Sha;
// Package your build artifacts using the sha to name the package
Package(sha);
}
Note: url can either point to:
a remote http url (http://www.example.com/repo.git)
a location on the CI server (file:///C:/My%20Documents/repo.git)
a location on the network (file://server/repos/repo.git)
I have been using LibGit2Sharp for quite some time, and it is good.
Below is a sample, which will iterate through the commits in your url.
Note: I had to do a clone, not sure if there is a better way:
string url = "http://github.com/libgit2/TestGitRepository";
using (Repository repo = Repository.Clone(url, #"C:\Users\Documents\test"))
{
foreach (var commit in repo.Commits)
{
var commitId = commit.Id;
var commitRawId = commitId.RawId;
var commitSha = commitId.Sha; //0ab936416fa3bec6f1bf3d25001d18a00ee694b8
var commitAuthorName = commit.Author.Name;
commits.Add(commit);
}
}

Categories