Read the files at the spesific commit with libgit2sharp - libgit2sharp

There is a bare repository, I have a commit id, and want to read all the files at that commit without cloning.
This repository.Lookup<Tree>(repository.Commits.First().Tree.Sha) code give me only the files that are in the commit but I want also other files that exists at that level.
How to do that?

My understanding of your question is that you're willing to access the whole content of a commit, not only the first level of the commit. The code below will work against a bare (or a standard) repository and will allow one to recursively access and examine the content of a commit.
In order to make it easier for you to test drive it, it dumps information (git object meta data along with blob content) in the console output.
RecursivelyDumpTreeContent(repo, "", commit.Tree);
[...]
private void RecursivelyDumpTreeContent(IRepository repo, string prefix, Tree tree)
{
foreach (var treeEntry in tree)
{
var path = prefix + treeEntry.Name;
var gitObject = treeEntry.Target;
var meta = repo.ObjectDatabase.RetrieveObjectMetadata(gitObject.Id);
Console.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}", gitObject.Id, treeEntry.Mode, treeEntry.TargetType, meta.Size, path);
if (treeEntry.TargetType == TreeEntryTargetType.Tree)
{
RecursivelyDumpTreeContent(repo, path + "/", (Tree)gitObject);
}
if (treeEntry.TargetType == TreeEntryTargetType.Blob)
{
Console.WriteLine((((Blob)gitObject).GetContentText()));
}
}
}
Would you precisely know the path of a specific file you'd like to access, use the indexer exposed by the Commit type in order to directly access the GitObject you're after.
For instance:
var blob = commit["path/to/my/file.txt"].Target as Blob;

Related

Populate data in to VSTS release summary tab

I am trying to create a release without mapping a existing build in TFS/VSTS and get data display in release summary once it is completed. in plain text steps are following
Release -> Empty Release Definition -> Add build task - > Create Release -> Deploy -> View Data in Summary Section
Summary data are view-able as expected without any issues with following two scenarios
Build - > Create build definition -> Add task - > Save and Queue build – Build Success - > View Summary Data
Release -> Empty Release Definition -> Link pre-defined Build definition -> Create Release -> provide successfully ran build version -> View Summary data.
As As per our understanding the issue occurs when we retrieving artifacts of the given release. We can retrieve results for builds but fail to do the same for releases. Below is the sample code we use to read release data. It will be much helpful if you can provide us guidance on retrieving artifacts details for given release. Right now we use following code in the client side for retrieving release artifacts but it complains release.artifacts is undefined. We have verified that the attachment file is saved to the given file location.
var c = VSS.getConfiguration();
c.onReleaseChanged(function (release) {
release.artifacts.forEach(function (art) {
var buildid = art.definitionReference.version.id;
// rest of the code is removed here
});
});
below are the references we followed to find solution,
https://github.com/Microsoft/vsts-tasks/blob/master/docs/authoring/commands.md
How to retrieve build attachment from VSTS release summary tab
https://github.com/Microsoft/vsts-extension-samples/blob/master/release-management/deployment-status-enhancer/scripts/main.js
https://github.com/Microsoft/vsts-extension-samples/blob/master/release-management/deployment-status-enhancer/index.html
https://www.visualstudio.com/en-us/docs/integrate/extensions/reference/client/core-sdk#method_getConfiguration
I was able to figure out an answer for this issue. I am herewith sharing same for others reference.
If we don’t link an artifact(build definition), then the artifacts for the release/release definition will not be filled with the data, so we won’t be able to refer to the attachment that got uploaded as part of the build.
Hence as per current API implementation, Below are the steps to follow to achieve this requirenment.
Writing data in to log while extension run as build task
Read above data once build completes (in client side)
Display retrieved (processed if required) data in release tab.
I found below code which explains retrieving data from log (reference : https://github.com/Dynatrace/Dynatrace-AppMon-TFS-Integration-Plugin/blob/master/src/enhancer/dynatrace-testautomation.ts)
public initialize(): void {
super.initialize();
// Get configuration that's shared between extension and the extension host
var sharedConfig: TFS_Release_Extension_Contracts.IReleaseViewExtensionConfig = VSS.getConfiguration();
if(sharedConfig) {
// register your extension with host through callback
sharedConfig.onReleaseChanged((release: TFS_Release_Contracts.Release) => {
// get the dynatraceTestRun attachment from the build
var rmClient = RM_Client.getClient();
var LOOKFOR_TASK = "Collect Dynatrace Testrun Results";
var LOOKFOR_TESTRUNDATA = "\"testRunData\":";
var drcScope = this;
release.environments.forEach(function (env) {
var _env = env;
//project: string, releaseId: number, environmentId: number, taskId: number
rmClient.getTasks(VSS.getWebContext().project.id, release.id, env.id).then(function(tasks){
tasks.forEach(function(task){
if (task.name == LOOKFOR_TASK){
rmClient.getLog(VSS.getWebContext().project.id, release.id, env.id, task.id).then(function(log){
var iTRD = log.indexOf(LOOKFOR_TESTRUNDATA);
if (iTRD > 0){
var testRunData = JSON.parse(log.substring(iTRD + LOOKFOR_TESTRUNDATA.length, log.indexOf('}',iTRD)+1));
drcScope.displayDynatraceTestRunData.bind(drcScope);
drcScope.displayDynatraceTestRunData(_env.name, testRunData);
}
});
}
});
});
});
});
sharedConfig.onViewDisplayed(() => {
VSS.resize();
});
}

Copying default external configuration on first run of Grails web app

In our Grails web applications, we'd like to use external configuration files so that we can change the configuration without releasing a new version. We'd also like these files to be outside of the application directory so that they stay unchanged during continuous integration.
The last thing we need to do is to make sure the external configuration files exist. If they don't, then we'd like to create them, fill them with predefined content (production environment defaults) and then use them as if they existed before. This allows any administrator to change settings of the application without detailed knowledge of the options actually available.
For this purpose, there's a couple of files within web-app/WEB-INF/conf ready to be copied to the external configuration location upon the first run of the application.
So far so good. But we need to do this before the application is initialized so that production-related modifications to data sources definitions are taken into account.
I can do the copy-and-load operation inside the Config.groovy file, but I don't know the absolute location of the WEB-INF/conf directory at the moment.
How can I get the location during this early phase of initialization? Is there any other solution to the problem?
There is a best practice for this.
In general, never write to the folder where the application is deployed. You have no control over it. The next rollout will remove everything you wrote there.
Instead, leverage the builtin configuration capabilities the real pro's use (Spring and/or JPA).
JNDI is the norm for looking up resources like databases, files and URL's.
Operations will have to configure JNDI, but they appreciate the attention.
They also need an initial set of configuration files, and be prepared to make changes at times as required by the development team.
As always, all configuration files should be in your source code repo.
I finally managed to solve this myself by using the Java's ability to locate resources placed on the classpath.
I took the .groovy files later to be copied outside, placed them into the grails-app/conf directory (which is on the classpath) and appended a suffix to their name so that they wouldn't get compiled upon packaging the application. So now I have *Config.groovy files containing configuration defaults (for all environments) and *Config.groovy.production files containing defaults for production environment (overriding the precompiled defaults).
Now - Config.groovy starts like this:
grails.config.defaults.locations = [ EmailConfig, AccessConfig, LogConfig, SecurityConfig ]
environments {
production {
grails.config.locations = ConfigUtils.getExternalConfigFiles(
'.production',
"${userHome}${File.separator}.config${File.separator}${appName}",
'AccessConfig.groovy',
'Config.groovy',
'DataSource.groovy',
'EmailConfig.groovy',
'LogConfig.groovy',
'SecurityConfig.groovy'
)
}
}
Then the ConfigUtils class:
public class ConfigUtils {
// Log4j may not be initialized yet
private static final Logger LOG = Logger.getGlobal()
public static def getExternalConfigFiles(final String defaultSuffix, final String externalConfigFilesLocation, final String... externalConfigFiles) {
final def externalConfigFilesDir = new File(externalConfigFilesLocation)
LOG.info "Loading configuration from ${externalConfigFilesDir}"
if (!externalConfigFilesDir.exists()) {
LOG.warning "${externalConfigFilesDir} not found. Creating..."
try {
externalConfigFilesDir.mkdirs()
} catch (e) {
LOG.severe "Failed to create external configuration storage. Default configuration will be used."
e.printStackTrace()
return []
}
}
final def cl = ConfigUtils.class.getClassLoader()
def result = []
externalConfigFiles.each {
final def file = new File(externalConfigFilesDir, it)
if (file.exists()) {
result << file.toURI().toURL()
return
}
final def error = false
final def defaultFileURL = cl.getResource(it + defaultSuffix)
final def defaultFile
if (defaultFileURL) {
defaultFile = new File(defaultFileURL.toURI())
error = !defaultFile.exists();
} else {
error = true
}
if (error) {
LOG.severe "Neither of ${file} or ${defaultFile} exists. Skipping..."
return
}
LOG.warning "${file} does not exist. Copying ${defaultFile} -> ${file}..."
try {
FileUtils.copyFile(defaultFile, file)
} catch (e) {
LOG.severe "Couldn't copy ${defaultFile} -> ${file}. Skipping..."
e.printStackTrace()
return
}
result << file.toURI().toURL()
}
return result
}
}

Resolving merge conflicts in the TFS API

I am trying to resolve merge conflicts with the TFS API and am struggling. My issue is as follows:
Merge from trunk -> branch
Conflict occurs during merge (same line of a file edited in both)
At this point I simply want to supply my own text for what the merged file should look like and then mark it as resolved.
I can't work out how to do this. See code below:
// merge was performed and there are conflicts to resolve
Conflict[] conflicts = ws.QueryConflicts(new string[] {"$/ProjectName/solution/"}, true);
Console.WriteLine(" - Conflicts: {0}", conflicts.Count());
if (conflicts.Any())
{
Console.WriteLine(" - Resolving conflicts");
// There are conflicts to deal with
foreach (Conflict conflict in conflicts)
{
// Attempt to perform merge internally
ws.MergeContent(conflict, false);
if (conflict.ContentMergeSummary.TotalConflicting > 0)
{
// Conflict was not resolved
// Manually resolve
// PROBLEM IS HERE <--------------
var allLines = File.ReadAllLines(conflict.TargetLocalItem).ToList();
allLines.Add("This is the MANUAL merge result");
File.WriteAllLines(conflict.TargetLocalItem, allLines.ToArray());
}
//conflict was resolved
conflict.Resolution = Resolution.AcceptMerge;
ws.ResolveConflict(conflict);
}
}
Checkin(ws, "Merge comment");
Console.WriteLine("Done");
As you can see I am trying to manually edit the target file, but this does not work. I can't seem to work out what file I should be editing on the conflict object to manually perform the merge.
One commenter has asked me to elaborate on the issues with the code above:
At the point of the comment "PROBLEM IS HERE", the target file still has a read only flag on it, so I cannot edit the file.
if I remove the readonly flag with some code and continue with the code, the resulting file does not contain my new line, but contains a diff style text with all the comments in. I can elaborate on this if it doesn't make sense.
If I pend an edit on the TargetLocalItem, this does nothing. No edit is pended.
This tells me that editing the target file in the local workspace is not the correct action.
Essentially I am trying to mimic what an external tool does, but my program will be the external tool and provide the merged text.
Try this
string tmpOutFile = Path.GetTempFileName();
conflict.MergedFileName = tmpOutFile;
// do stuff with tmpOutFile
conflict.Resolution = Resolution.AcceptMerge;
ws.ResolveConflict(conflict);

Programmatically delete a TFS branch

I want to programmatically delete a branch in TFS that was create automatically.
There is an existing method "ICommonStructureService.DeleteBranches" that should do the work.
My problem is that the method requires a parameter "string[] nodeUris" that specifies the branch to delete using a "vstfs://... " URI and I just don't know how to get that for my branch.
What I need is something like:
var projectCollection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri <myCollectionUrl>));
var cssService = projectCollection.GetService<ICommonStructureService3>();
var project = cssService.GetProjectFromName(<myProjectName>);
But how can I get the Branch Uri from there?
Meanwhile I found a solution. For deleting the branches I am using
versionControl.Destroy(new ItemSpec(myBranchPath, RecursionType.Full), VersionSpec.Latest, null, DestroyFlags.KeepHistory);
This does exactly what I needed.
versionControl is of type VersionControlServer and must be initialized using the Team Collection
Deleting a branch in version control is like deleting any other version control item. You will need to pend a delete with Workspace.PendDelete on the Item.
The method you reference is wholly unrelated to version control, it's part of the TFS common structure service, which controls the "areas and iterations" that TFS work items can be assigned to.
In short, there's no way to perform any sort of version control operations against the common structure service. You delete a branch by creating a Workspace against a VersionControlServer, pending a delete and then checking in your pending changes.
I agree to Edward Thomson about using Destroy command. So I followed on advice from him and came up with following,
public void DeleteBranch(string path)
{
var vcs = GetVersionControlServer();
var itemSpec = new ItemSpec(path, RecursionType.Full);
var itemSpecs = new[] {itemSpec};
var workSpace = GetOrCreateWorkSpace(vcs);
try
{
workSpace.Map(path, #"c:\Temp\tfs");
var request = new GetRequest(itemSpec, VersionSpec.Latest);
workSpace.Get(request, GetOptions.GetAll | GetOptions.Overwrite);
workSpace.PendDelete(path, RecursionType.Full);
var pendingchanges = workSpace.GetPendingChanges(itemSpecs);
workSpace.CheckIn(pendingchanges, "Deleting The Branch");
}
finally
{
if (workSpace != null)
{
workSpace.Delete();
}
}
}
If there is a neat way to do the same than I am looking forward to it. This is bit slow as it does too many things,
Creates Temp Workspace
Gets All changes to that
Performs Delete to whole change set
checks it in
Cleans up the workspace

TFS2010 Custom Build Activity : to Merge branches

I'm working on customizing our build activity. I'd like to have your help for an issue.
Following is our version control hierarchy.
Main
|- Dev
|- QA
we are working on Dev branch and while taking the build we need to merge Dev branch to Main then to QA.
Main is the root branch as you might know.
In our build template, I've added two custom activities to merge one from Dev to Main and another one to merge from Main to QA. Following is the code for the custom activity.
protected override string Execute(CodeActivityContext context)
{
string lstrStatus = string.Empty;
string lstrSourceBranchPath = context.GetValue(this.SourceBranchPath);
string lstrTargetBranchPath = context.GetValue(this.TargetBranchPath);
// Obtain the runtime value of the input arguments
Workspace workspace = context.GetValue(this.Workspace);
GetStatus status = workspace.Merge(lstrSourceBranchPath,
lstrTargetBranchPath,
null,
null,
LockLevel.None,
RecursionType.Full,
MergeOptions.None);
// resolve the conflicts, if any
if (status.NumConflicts > 0)
{
Conflict[] conflicts = workspace.QueryConflicts(new string[]
{ lstrTargetBranchPath }, true);
foreach (Conflict conflict in conflicts)
{
conflict.Resolution = Resolution.AcceptTheirs;
workspace.ResolveConflict(conflict);
}
}
// checkin the changes
PendingChange[] pendingChanges = workspace.GetPendingChanges();
if (pendingChanges != null && pendingChanges.Length > 0)
{
workspace.CheckIn(pendingChanges, "Merged by MERGE BRANCHES activity");
}
return lstrStatus;
}
Problem is, merging happens perfectly in the server. But, it's not getting reflected in the local folder. I tried to add SyncWorkspace activity after each Merge custom activity. Still not working.
My guess was that a SyncWorkspace should be the only thing to do.
You could try doing a RevertWorkspace before that.
EDIT
After you now stated that even this wouldn't work, I would generate a bug against MS at least to get an official answer.
In the meanwhile you can try with the following method, which I absolutely see as an overkill: Once you have checked in, redo all the steps within sequence Initialize Workspace.
If even that doesn't work I'd consider two different builds, one that does your merge & one that does the actual build. You can then organize a scheme where your first build, once it's done, triggers the second one. Here is a good resource for that.

Resources