How to determine if need to git_commit_create() after the merge()? - merge

When I use libgit2 to update data, pull seem to create unnecessary short-term branches and merges which are a headache to resolve. and each merge produced a new oid, so there should be a function to determine if a merge commit is needed before the merge commit. wondering if it is a good practice, then what is the best way to do it?

If you want to analyze whether a merge is needed between two branches, or if instead it is fast-forwardable, use the git_merge_analysis function:
git_merge_analysis_t analysis;
git_merge_preference_t preference;
int error = git_merge_analysis(&analysis, &preference, repo, merge_heads, merge_head_len);
if (analysis == GIT_MERGE_ANALYSIS_FASTFORWARD) {
/* You can simply do a fast-forward */
} else {
/* You need to do a true merge */
}
You may wish to examine the preference to know whether the user has asked to never fast-forward.

Related

How to force merge (with conflict markers) in JGit?

Our application needs to use different merge strategies when fetching / merging with JGit. For instance, for any files in conflict that are JSON files, we want to revert any local changes and get THEIRS. If they're not JSON files, we want to keep local changes and merge, even keeping the conflict markers if there are conflicts. However, no matter how I set the merge strategy, I get a CheckoutConflictException (which stops the merge) even if normal command-line git would have done the merge. What am I doing wrong?
public String update() throws Exception {
// We fetch manually and do a dry-run merge to catch any conflicts, so we can reset them
fetch();
Ref fetchHead = this.repo().getRepository().findRef("FETCH_HEAD");
try {
MergeResult mergeResult = this.repo().merge()
.setStrategy(MergeStrategy.RECURSIVE)
.setCommit(false)
.setFastForward(MergeCommand.FastForwardMode.FF)
.include(fetchHead)
.call();
} catch (CheckoutConflictException cce) {
List<String> conflicts = cce.getConflictingPaths();
if (conflicts != null && conflicts.size() > 0) {
for (String file : conflicts) {
if (file.endsWith(".json")) {
revert(Paths.get(file));
}
}
}
}
/// ... and now that we've reverted any conflicted JSON files, we can try the merge for real
MergeResult mergeResult = this.repo().merge()
.setStrategy(MergeStrategy.RECURSIVE)
.setCommit(true)
.setFastForward(MergeCommand.FastForwardMode.FF)
.include(fetchHead)
.call();
return mergeResult.getMergeStatus().toString();
}
I was able to get the behavior I wanted using stashCreate() and stashApply(). Specifically, stashCreate() to stash any changes left after reverting any JSON files, then using PullCommand (not MergeCommand) to pull any new changes, then using stashApply() to apply any stashed local changes. I tested it with no conflicts, conflicts in JSON files, conflicts in other files where different parts of the file were changed between local and remote, and other files where the same line(s) were changed. It worked in all of the above cases.

how to get a list of commits from refChanges in Atlassian Stash Pre Receive Repository Hook

Im trying to write a stash plugin that will iterate through the commits in a change set pushed to stash in a Pre Receive Repository Hook.
The API passes a Collection of refChange in the onReceive method.
public boolean onReceive(RepositoryHookContext context, Collection<RefChange> refChanges, HookResponse hookResponse)
if I make 3 commits then push I get one RefChange which looks like this
refId = refs/heads/master
fromHash = ded3e4583653f14892cc3e8a898ba74ee75e1a58 // First Commit in change set
toHash = ae017dcdadf7ca69617fb05f6905cccfe2aa4229 // Most recent commit
type = "UPDATE"
Id like to get a collection of all the commits so that I can get all the commit messages.
I'm looking at com.atlassian.stash.commit.CommitService getCommit and getCommits. I think I need to getCommitsBetween but can't quite figure out how to crate the GetCommitsBetween parameter needed from the RefChange I have.
Am I even heading down the right path here?
Even though the CommitsBetweenRequest page on the Atlassian Stash API documentation is one of the few pages with an explanation, it took some trial and error to figure this out. GetCommitsBetween works but here's the trick...
Set the commitsBetweenBuilder.exclude to the starting commit in the change set and commitsBetweenBuilder.include to the ending commit hash.
CommitsBetweenRequest.Builder commitsBetweenBuilder = new CommitsBetweenRequest.Builder(context.getRepository() );
commitsBetweenBuilder.exclude(refChange.getFromHash()); //Starting with
commitsBetweenBuilder.include(refChange.getToHash()); // ending with
PageRequest pageRequest = new PageRequestImpl(0,6);
Page<Commit> commits = commitService.getCommitsBetween(commitsBetweenBuilder.build(), pageRequest);
//TODO: handle Pages
for (Commit commit : commits.getValues()) {
hookResponse.out().println("Message = " + commit.getMessage() + "\n");
}
I wasn't able to get the dependency injection working for the CommitService. Spring for some reason wasn't able to find it, when trying to run in locally ???
I did getting it working using the component locator.
CommitService commitService = ComponentLocator.getComponent(CommitService.class);

CQ5 / AEM5.6 Workflow: Access workflow instance properties from inside OR Split

TL;DR version:
In CQ workflows, is there a difference between what's available to the OR Split compared to the Process Step?
Is it possible to access the /history/ nodes of a workflow instance from within an OR Split?
How?!
The whole story:
I'm working on a workflow in CQ5 / AEM5.6.
In this workflow I have a custom dialog, which stores a couple of properties on the workflow instance.
The path to the property I'm having trouble with is: /workflow/instances/[this instance]/history/[workItem id]/workItem/metaData and I've called the property "reject-or-approve".
The dialog sets the property fine (via a dropdown that lets you set it to "reject" or "approve"), and I can access other properties on this node via a process step (in ecma script) using:
var actionReason;
var history = workflowSession.getHistory(workItem.getWorkflow());
// loop backwards through workItems
// and as soon as we find a Action Reason that is not empty
// store that as 'actionReason' and break.
for (var index = history.size() - 1; index >= 0; index--) {
var previous = history.get(index);
var tempActionReason = previous.getWorkItem().getMetaDataMap().get('action-message');
if ((tempActionReason != '')&&(tempActionReason != null)) {
actionReason = tempActionReason;
break;
}
}
The process step is not the problem though. Where I'm having trouble is when I try to do the same thing from inside an OR Split.
When I try the same workflowSession.getHistory(workItem.getWorkflow()) in an OR Split, it throws an error saying workItem is not defined.
I've tried storing this property on the payload instead (i.e. storing it under the page's jcr:content), and in that case the property does seem to be available to the OR Split, but my problems with that are:
This reject-or-approve property is only relevant to the current workflow instance, so storing it on the page's jcr:content doesn't really make sense. jcr:content properties will persist after the workflow is closed, and will be accessible to future workflow instances. I could work around this (i.e. don't let workflows do anything based on the property unless I'm sure this instance has written to the property already), but this doesn't feel right and is probably error-prone.
For some reason, when running through the custom dialog in my workflow, only the Admin user group seems to be able to write to the jcr:content property. When I use the dialog as any other user group (which I need to do for this workflow design), the dialog looks as though it's working, but never actually writes to the jcr:content property.
So for a couple of different reasons I'd rather keep this property local to the workflow instance instead of storing it on the page's jcr:content -- however, if anyone can think of a reason why my dialog isn't setting the property on the jcr:content when I use any group other than admin, that would give me a workaround even if it's not exactly the solution I'm looking for.
Thanks in advance if anyone can help! I know this is kind of obscure, but I've been stuck on it for ages.
a couple of days ago i ran into the same issue. The issue here is that you don't have the workItem object, because you don't really have an existing workItem. Imagine the following: you are going through the workflow, you got a couple of workItems, with means, either process step, either inbox item. When you are in an or split, you don't have existing workItems, you can ensure by visiting the /workItems node of the workflow instance. Your workaround seems to be the only way to go through this "issue".
I've solved it. It's not all that elegant looking, but it seems to be a pretty solid solution.
Here's some background:
Dialogs seem to reliably let you store properties either on:
the payload's jcr:content node (which wasn't practical for me, because the payload is locked during the workflow, and doesn't let non-admins write to its jcr:content)
the workItem/metaData for the current workflow step
However, Split steps don't have access to workItem. I found a fairly un-helpful confirmation of that here: http://blogs.adobe.com/dmcmahon/2013/03/26/cq5-failure-running-script-etcworkflowscriptscaworkitem-ecma-referenceerror-workitem-is-not-defined/
So basically the issue was, the Dialog step could store the property, but the OR Split couldn't access it.
My workaround was to add a Process step straight after the Dialog in my workflow. Process steps do have access to workItem, so they can read the property set by the Dialog. I never particularly wanted to store this data on the payload's jcr:content, so I looked for another location. It turns out the workflow metaData (at the top level of the workflow instance node, rather than workItem/metaData, which is inside the /history sub-node) is accessible to both the Process step and the OR Split. So, my Process step now reads the workItem's approveReject property (set by the Dialog), and then writes it to the workflow's metaData node. Then, the OR Split reads the property from its new location, and does its magic.
The way you access the workflow metaData from the Process step and the OR Split is not consistent, but you can get there from both.
Here's some code: (complete with comments. You're welcome)
In the dialog where you choose to approve or reject, the name of the field is set to rejectApprove. There's no ./ or anything before it. This tells it to store the property on the workItem/metaData node for the current workflow step under /history/.
Straight after the dialog, a Process step runs this:
var rejectApprove;
var history = workflowSession.getHistory(workItem.getWorkflow());
// loop backwards through workItems
// and as soon as we find a rejectApprove that is not empty
// store that as 'rejectApprove' and break.
for (var index = history.size() - 1; index >= 0; index--) {
var previous = history.get(index);
var tempRejectApprove = previous.getWorkItem().getMetaDataMap().get('rejectApprove');
if ((tempRejectApprove != '')&&(tempRejectApprove != null)) {
rejectApprove = tempRejectApprove;
break;
}
}
// steps up from the workflow step into the workflow metaData,
// and stores the rejectApprove property there
// (where it can be accessed by an OR Split)
workItem.getWorkflowData().getMetaData().put('rejectApprove', rejectApprove);
Then after the Process step, the OR Split has the following in its tabs:
function check() {
var match = 'approve';
if (workflowData.getMetaData().get('rejectApprove') == match) {
return true;
} else {
return false;
}
}
Note: use this for the tab for the "approve" path, then copy it and replace var match = 'approve' with var match = 'reject'
So the key here is that from a Process step:
workItem.getWorkflowData().getMetaData().put('rejectApprove', rejectApprove);
writes to the same property that:
workflowData.getMetaData().get('rejectApprove') reads from when you execute it in an OR Split.
To suit our business requirements, there's more to the workflow I've implemented than just this, but the method above seems to be a pretty reliable way to get values that are entered in a dialog, and access them from within an OR Split.
It seems pretty silly that the OR Split can't access the workItem directly, and I'd be interested to know if there's a less roundabout way of doing this, but for now this has solved my problem.
I really hope someone else has this same problem, and finds this useful, because it took me waaay to long to figure out, to only apply it once!

TFS 2010 API - Get work items from merge

I need to send an email on completion of a build in TFS 2010 which details the work items associated with check-ins that have been compiled as part of this build. This works no problem via use of the associatedChangesets variable available in the build workflow.
However, in a production situation, we will merge changes from our Development branch into a Release branch. At this point, the build considers there to have only been one change - which is the aforementioned merging of Development into Release. Obviously this is fairly useless as we need to find out which changes where made in the branch that was merged in, and the work items associated.
Does anyone know how to accomplish this using the TFS 2010 API? It seems to be fairly poorly documented from an API perspective. I know you can expand the merge history node in VS2010 but obviously this is no good as this data needs to be collected programatically so a report email can be sent.
OK... I think I found a solution to this although it's clunky and truth be told I'm not exactly sure how it works. But here goes - maybe it will point someone in the right direction.
var associatedWorkItems = new List<WorkItem>();
//Passed in from the build workflow (this variable is available under the 'Run On Agent' sequence as 'associatedChangesets'
IList<Changeset> associatedChangesets = context.GetValue(BuildAssociatedChangesets);
if (associatedChangesets.Count > 0)
{
var projectCollection =
new TfsTeamProjectCollection(new Uri("http://localhost:8080/tfs/DefaultCollection"));
VersionControlServer versionControlServer = projectCollection.GetService<VersionControlServer>();
foreach (var changeset in associatedChangesets)
{
//In order to view the individual changes, load the changeset directly from the VCS.
Changeset localChangeset = versionControlServer.GetChangeset(changeset.ChangesetId);
foreach (Change change in localChangeset.Changes)
{
//Find out what was merged in.
ChangesetMerge[] mergedChangesets = versionControlServer.QueryMerges(
null,
null,
change.Item.ServerItem,
new ChangesetVersionSpec(localChangeset.ChangesetId),
new ChangesetVersionSpec(localChangeset.ChangesetId),
null,
RecursionType.Full);
//Extract work item information from changesets being identified as merged.
foreach (var changesetMerge in mergedChangesets)
{
Changeset actualChange = versionControlServer.GetChangeset(changesetMerge.SourceVersion);
foreach (WorkItem item in actualChange.WorkItems)
{
if (!associatedWorkItems.Exists(w => w.Id == item.Id))
{
associatedWorkItems.Add(item);
}
}
}
}
}
}
Don't ask me exactly how QueryMerges works but all I'm doing here it saying show me what what merged as a part of a changeset checked in. You'll notice that the parameters ChangesetVersionSpec are the same - this means we're just looking at merges from this one changeset.
You'll get back an array of ChangesetMerge objects from QueryMerges(). In the ChangesetMerge class there is a property called SourceVersion - this is the ChangesetId of the original changeset merged in. Once we've got that we can use the VersionControlServer.GetChangeset() method to load the individual set and extract the WorkItem. This is then added to a list of WorkItems which can be manipulated in any way you want (in my case an email). I also used the .Exists() check to make sure the same WorkItem doesn't get recorded twice.
Note that even though you have the collection associatedChangesets from the build workflow, for some reason (for me at least), the Changes[] property inside associatedChangesets was never populated (hence loading each individual changeset using the VersionControlServer.GetChangeset() method as this seems to actually populate all the fields we need.
Like I say, 1. this is a clunky solution (lots of looping - some of which is probably unecessary), 2. I don't fully understand how this works although it seems to produce the required results - I came to this conclusion by doing a lot testing and debugging) and finally - it's the best I could come up with based on the woeful documentation provided by Microsoft.
Hope it helps someone!

How two people, concurrently editing the same file is handled?

I believe the title says it. I'm new to source control thingy.
So, let's say I have two developers working on the same project and they started editing the same file(s) at the same time then everyone of them send the new version at a slightly different time. From what I understand the one who sends the changes last will have his changes kept, the other one's code will be in the archive only!!!
Is that correct?
Please clarify. Thanks.
No, that's not quite correct. It depends somewhat on which version control software you're using, but I like Git so I'll talk about that.
Suppose we have a file Foo.java:
class Foo {
public void printAWittyMessage() {
// TODO: Be witty
}
}
Alice and Bob both modify the file. Alice does this:
class Foo {
public void printAWittyMessage() {
System.out.println("Alice is the coolest");
}
}
and Bob does this:
class Foo {
public void printAWittyMessage() {
System.out.println("Alice is teh suk");
}
}
Alice checks her version in first. When Bob attempts to check his in, Git will warn him that there is a conflict and won't allow the commit to be pushed into the main repository. Bob has to update his local repository and fix the conflict. He'll get something like this:
class Foo {
public void printAWittyMessage() {
<<<<< HEAD:<some git nonsense>
System.out.println("Alice is the coolest");
=====
System.out.println("Alice is teh suk");
>>>>> blahdeblahdeblah:<some more git nonsense>
}
}
The <<<<<, ===== and >>>>> markers show which lines were changed simultaneously. Bob must resolve the conflict in some sensible way, remove the markers, and commit the result.
So what eventually lives in the repository is:
Original version -> Alice's version -> Bob's conflict-fixed version.
To summarise: the first to commit gets in without any problems, the second to commit must resolve the conflict before getting into the repository. You should never end up with someone's changes being clobbered automatically. Obviously Bob can resolve the conflict incorrectly but the beauty of version control is that you can roll back the incorrect fix and repair it.
Much depends on the system you're using.
However in the common case is: who commits his changes second would have to perform a "merge" operation. Meaning s/he would need to compare the two files and come up with a merged version. However (!) many popular system (including IDE) comes with smart tools to assist you doing that.
Here are some tools like that compared:
http://en.wikipedia.org/wiki/Comparison_of_file_comparison_tools