What's the fastest way to find Tags pointing to Commits? - libgit2

With libgit2sharp I would like to do the following:
foreach( Commit commit in repo.Commits )
{
// How to implement assignedTags?
foreach( Tag tag in commit.assignedTags ) {}
}
I want to get all tags assigned to the current commit. Whats the best way to do that? Iterate through all Tags and see if tag.Target.Sha == commit.Sha? Thats not very performant. Is there another way?

So I want to get all tags assigned to the current commit. Whats the best way to do that? Iterate through all Tags and see if tag.Target.Sha == commit.Sha? Thats not very performant. Is there another way?
There are two things to take into account when it comes to Tags.
A Tag can point to something else than a Commit (A Tree or a Blob, for instance)
A Tag can point to another Tag (chained annotated tags)
The code below should fit your need by taking these points above into account.
Note: repo.Commits will only enumerate the commits reachable from the current branch (HEAD). The code below
extends this to easily browse all the reachable commits.
...
using (var repo = new Repository("Path/to/your/repo"))
{
// Build up a cached dictionary of all the tags that point to a commit
var dic = TagsPerPeeledCommitId(repo);
// Let's enumerate all the reachable commits (similarly to `git log --all`)
foreach (Commit commit in repo.Commits.QueryBy(new CommitFilter {Since = repo.Refs}))
{
foreach (var tags in AssignedTags(commit, dic))
{
Console.WriteLine("Tag {0} points at {1}", tags.Name, commit.Id);
}
}
}
....
private static IEnumerable<Tag> AssignedTags(Commit commit, Dictionary<ObjectId, List<Tag>> tags)
{
if (!tags.ContainsKey(commit.Id))
{
return Enumerable.Empty<Tag>();
}
return tags[commit.Id];
}
private static Dictionary<ObjectId, List<Tag>> TagsPerPeeledCommitId(Repository repo)
{
var tagsPerPeeledCommitId = new Dictionary<ObjectId, List<Tag>>();
foreach (Tag tag in repo.Tags)
{
GitObject peeledTarget = tag.PeeledTarget;
if (!(peeledTarget is Commit))
{
// We're not interested by Tags pointing at Blobs or Trees
continue;
}
ObjectId commitId = peeledTarget.Id;
if (!tagsPerPeeledCommitId.ContainsKey(commitId))
{
// A Commit may be pointed at by more than one Tag
tagsPerPeeledCommitId.Add(commitId, new List<Tag>());
}
tagsPerPeeledCommitId[commitId].Add(tag);
}
return tagsPerPeeledCommitId;
}

Here is another version of nulltoken's answer but with using ILookupclass instead of dictionary. A bit nicer IMO:
private static ILookup<ObjectId, Tag> CreateCommitIdToTagLookup(Repository repo)
{
var commitIdToTagLookup =
repo.Tags
.Select(tag => new { Commit = tag.PeeledTarget as Commit, Tag = tag })
.Where(x => x.Commit != null)
.ToLookup(x => x.Commit.Id, x => x.Tag);
return commitIdToTagLookup;
}
and simple usage example:
using (var repo = new Repository("Path/to/your/repo"))
{
var commitIdToTagLookup = CreateCommitIdToTagLookup(repo);
foreach (var commit in repo.Commits)
{
foreach (var tag in commitIdToTagLookup[commit.Id])
{
Console.WriteLine($"Tag {tag.FriendlyName} points at {commit.Id}");
}
}
}

Related

Adding MVC4 Display Modes Dynamically

In Global.asax, I want to add a number of display modes dynamically, based on a string array I have, thus:
foreach (string displayModeId in myDisplayModeIds)
{
modes.Insert(0, new DefaultDisplayMode(displayModeId)
{
ContextCondition = context =>
context.Request["DisplayMode"] == displayModeId
}
);
}
Of course this doesn't work, because displayModeId needs to be a constant within the scope of the ContextCondition.
Any help appreciated.
You could capture it in a closure so that it doesn't run away from you in the loop:
foreach (string displayModeId in myDisplayModeIds)
{
string dmId = displayModeId;
modes.Insert(0, new DefaultDisplayMode(displayModeId)
{
ContextCondition = context => context.Request["DisplayMode"] == dmId
});
}

Copying sitecore rendering to new template programmatically using renderingDefinition.ItemId?

I have a custom sitecore button which changes the template of the current item, simple enough.
However as part of this I'm trying to also migrate the renderings of the old layout to a new layout if it's of a certain sublayout type by ItemId. However the ItemId that is returned is always null, the only value I get back from the RenderingDefinition is the UniqueId.
What am I doing wrong?
I have used this blog post as a guide.
The Code
public class ConvertToNewTemplateCommand : Command
{
protected void Run(ClientPipelineArgs args)
{
if (!SheerResponse.CheckModified())
return;
Item item = Context.ContentDatabase.Items[args.Parameters["id"]];
if (args.IsPostBack)
{
if (args.Result == "yes")
{
//Get current layout details
var originalLayoutXml = item[FieldIDs.LayoutField];
//Get new template
TemplateItem hubTemplate = Context.ContentDatabase.GetTemplate("some guid...");
//Change template
item.ChangeTemplate(hubTemplate);
//Reset laytout
ResetLayout(item);
//Get reset layout
var newLayoutXml = item[FieldIDs.LayoutField];
//Add all the module containers to the new layout in the central column
MoveModuleContainers(item, originalLayoutXml, newLayoutXml);
}
}
}
private void MoveModuleContainers(Item item, string oldXml, string newXml)
{
var oldLayout = LayoutDefinition.Parse(oldXml);
var newLayout = LayoutDefinition.Parse(newXml);
bool updated = false;
var oldRenderings = (oldLayout.Devices[0] as DeviceDefinition).Renderings;
var newRenderings = (newLayout.Devices[0] as DeviceDefinition).Renderings;
foreach (RenderingDefinition rendering in oldRenderings)
{
// Here is where the rendering.ItemID is always null
if (rendering != null && !String.IsNullOrEmpty(rendering.ItemID) && new Guid(rendering.ItemID) == new Guid("matching guid..."))
{
rendering.Placeholder = "middlecolumn";
newRenderings.Add(rendering);
updated = true;
}
}
if (updated)
{
// Save item...
}
}
}
I got onto Sitecore support in the end which informed me that I should use:
Sitecore.Data.Fields.LayoutField.GetFieldValue(item.Fields[Sitecore.FieldIDs.LayoutField])
instead of:
item[FieldIDs.LayoutField]
to get the items layoutField correctly. This results in the rendering values being parsed correctly and as they say the rest is history.

Generate test data in Raven DB

I am looking for a preferred and maintainable way of test data generation in Raven DB. Currently, our team does have a way to do it through .NET code. Example is provided.
However, i am looking for different options. Please share.
public void Execute()
{
using (var documentStore = new DocumentStore { ConnectionStringName = "RavenDb" })
{
documentStore.Conventions.DefaultQueryingConsistency = ConsistencyOptions.QueryYourWrites;
// Override the default key prefix generation strategy of Pascal case to lower case.
documentStore.Conventions.FindTypeTagName = type => DocumentConvention.DefaultTypeTagName(type).ToLower();
documentStore.Initialize();
InitializeData(documentStore);
}
}
Edit: Raven-overflow is really helpful. Thanks for pointing out to the right place.
Try checking out RavenOverflow. In there, I've got a FakeData project that has fake data (both hardcoded AND randomly generated). This can then be used in either my Tests project or the Main Website :)
Here's some sample code...
if (isDataToBeSeeded)
{
HelperUtilities.CreateSeedData(documentStore);
}
....
public static void CreateSeedData(IDocumentStore documentStore)
{
Condition.Requires(documentStore).IsNotNull();
using (IDocumentSession documentSession = documentStore.OpenSession())
{
// First, check to make sure we don't have any data.
var user = documentSession.Load<User>(1);
if (user != null)
{
// ooOooo! we have a user, so it's assumed we actually have some seeded data.
return;
}
// We have no users, so it's assumed we therefore have no data at all.
// So let's fake some up :)
// Users.
ICollection<User> users = FakeUsers.CreateFakeUsers(50);
StoreFakeEntities(users, documentSession);
// Questions.
ICollection<Question> questions = FakeQuestions.CreateFakeQuestions(users.Select(x => x.Id).ToList());
StoreFakeEntities(questions, documentSession);
documentSession.SaveChanges();
// Make sure all our indexes are not stale.
documentStore.WaitForStaleIndexesToComplete();
}
}
....
public static ICollection<Question> CreateFakeQuestions(IList<string> userIds, int numberOfFakeQuestions)
{
.... you get the idea .....
}

EntityFramework, Insert if not exist, otherwise update

I'm having a Entity-Set Countries, reflecting a database table '<'char(2),char(3),nvarchar(50> in my database.
Im having a parser that returns a Country[] array of parsed countries, and is having issues with getting it updated in the right way. What i want is: Take the array of countries, for those countries not already in the database insert them, and those existing update if any fields is different. How can this be done?
void Method(object sender, DocumentLoadedEvent e)
{
var data = e.ParsedData as Country[];
using(var db = new DataContractEntities)
{
//Code missing
}
}
I was thinking something like
for(var c in data.Except(db.Countries)) but it wount work as it compares on wronge fields.
Hope anyone have had this issues before, and have a solution for me. If i cant use the Country object and insert/update an array of them easy, i dont see much benefict of using the framework, as from performers i think its faster to write a custom sql script that inserts them instead of ect checking if an country is already in the database before inserting?
Solution
See answer of post instead.
I added override equals to my country class:
public partial class Country
{
public override bool Equals(object obj)
{
if (obj is Country)
{
var country = obj as Country;
return this.CountryTreeLetter.Equals(country.CountryTreeLetter);
}
return false;
}
public override int GetHashCode()
{
int hash = 13;
hash = hash * 7 + (int)CountryTreeLetter[0];
hash = hash * 7 + (int)CountryTreeLetter[1];
hash = hash * 7 + (int)CountryTreeLetter[2];
return hash;
}
}
and then did:
var data = e.ParsedData as Country[];
using (var db = new entities())
{
foreach (var item in data.Except(db.Countries))
{
db.AddToCountries(item);
}
db.SaveChanges();
}
I would do it straightforward:
void Method(object sender, DocumentLoadedEvent e)
{
var data = e.ParsedData as Country[];
using(var db = new DataContractEntities)
{
foreach(var country in data)
{
var countryInDb = db.Countries
.Where(c => c.Name == country.Name) // or whatever your key is
.SingleOrDefault();
if (countryInDb != null)
db.Countries.ApplyCurrentValues(country);
else
db.Countries.AddObject(country);
}
db.SaveChanges();
}
}
I don't know how often your application must run this or how many countries your world has. But I have the feeling that this is nothing where you must think about sophisticated performance optimizations.
Edit
Alternative approach which would issue only one query:
void Method(object sender, DocumentLoadedEvent e)
{
var data = e.ParsedData as Country[];
using(var db = new DataContractEntities)
{
var names = data.Select(c => c.Name);
var countriesInDb = db.Countries
.Where(c => names.Contains(c.Name))
.ToList(); // single DB query
foreach(var country in data)
{
var countryInDb = countriesInDb
.SingleOrDefault(c => c.Name == country.Name); // runs in memory
if (countryInDb != null)
db.Countries.ApplyCurrentValues(country);
else
db.Countries.AddObject(country);
}
db.SaveChanges();
}
}
The modern form, using later EF versions would be:
context.Entry(record).State = (AlreadyExists ? EntityState.Modified : EntityState.Added);
context.SaveChanges();
AlreadyExists can come from checking the key or by querying the database to see whether the item already exists there.
You can implement your own IEqualityComparer<Country> and pass that to the Except() method. Assuming your Country object has Id and Name properties, one example of that implementation could look like this:
public class CountryComparer : IEqualityComparer<Country>
{
public bool Equals(Country x, Country y)
{
return x.Name.Equals(y.Name) && (x.Id == y.Id);
}
public int GetHashCode(Country obj)
{
return string.Format("{0}{1}", obj.Id, obj.Name).GetHashCode();
}
}
and use it as
data.Countries.Except<Country>(db, new CountryComparer());
Although, in your case it looks like you just need to extract new objects, you can use var newCountries = data.Where(c => c.Id == Guid.Empty); if your Id is Guid.
The best way is to inspect the Country.EntityState property and take actions from there regarding on value (Detached, Modified, Added, etc.)
You need to provide more information on what your data collection contains i.e. are the Country objects retrieved from a database through the entityframework, in which case their context can be tracked, or are you generating them using some other way.
I am not sure this will be the best solution but I think you have to get all countries from DB then check it with your parsed data
void Method(object sender, DocumentLoadedEvent e)
{
var data = e.ParsedData as Country[];
using(var db = new DataContractEntities)
{
List<Country> mycountries = db.Countries.ToList();
foreach(var PC in data)
{
if(mycountries.Any( C => C.Name==PC.Name ))
{
var country = mycountries.Any( C => C.Name==PC.Name );
//Update it here
}
else
{
var newcountry = Country.CreateCountry(PC.Name);//you must provide all required parameters
newcountry.Name = PC.Name;
db.AddToCountries(newcountry)
}
}
db.SaveChanges();
}
}

'foreach' failing when using Parallel Task Library

The following code creates the correct number of files, but every file contains the contents of the first list. Can anyone spot what I've done wrong please?
private IList<List<string>> GetLists()
{
// Code omitted for brevity...
}
private void DoSomethingInParallel()
{
var lists = GetLists();
var tasks = new List<Task>();
var factory = new TaskFactory();
foreach (var list in lists)
{
tasks.Add(factory.StartNew(() =>
{
WriteListToLogFile(list);
}));
}
Task.WaitAll(tasks.ToArray());
}
The reason why is down to the way C# evaluates anonymous methods, they're not true closures. It really has nothing to do with the TPL. The following code prints out all d's. This is not what yoy would expect
List<Task> tasks = new List<Task>();
List<string> lists = new List<string>();
lists.AddRange(new string[] { "a", "b", "c", "d" });
foreach (var list in lists)
{
tasks.Add(Task.Factory.StartNew(() =>
{
Console.WriteLine(list);
}));
}
The reason is because the value of list when the anonymous method was created is not the one that gets evaluated in the method body. The value of list at the time the method was executed is used. You can force a fix for this by doing the following:
List<Task> tasks = new List<Task>();
List<string> lists = new List<string>();
lists.AddRange(new string[] { "a", "b", "c", "d" });
foreach (var list in lists)
{
var localList = list;
tasks.Add(Task.Factory.StartNew(() =>
{
Console.WriteLine(localList);
}));
}
You don't have to pass in the list value to the anonymous method explicitly.
This blog post goes into this in much more detail:
Link
Apologies for not replying to this earlier. I found a solution - although I don't understand why it works...
Originally, I had this ...
foreach (var list in lists)
{
tasks.Add(factory.StartNew(() =>
{
WriteListToLogFile(list);
}));
}
Changing the sequential foreach to a parallel foreach fixes the problem...
Parallel.ForEach<string>(lists, list =>
tasks.Add(factory.StartNew(() =>
{
WriteListToLogFile(list);
}));
);
I am not sure why you have a list for "tasks", you are only ever using one of them.
edit:
factory.StartNew Creates and starts a System.Threading.Tasks.Task!!
Thinking out loud:
so there is a separate task for each of the List<String> in its list which calls WriteListToLogFile?
I think you will need to use
ThreadPool.QueueUserWorkItem
in your code after task.Add
look at this example (see the accepted answer post) link
Ran into this same problem myself. I'm still not sure why it happens, but I was able to get it to work properly by passing in a state object
foreach (var list in lists)
{
tasks.Add(factory.StartNew((o) =>
{
var l = o as List<string>;
WriteListToLogFile(l);
}, list));
}