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

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);

Related

How to get contents of file in pull request from pygithub?

I'm not able to find how to get contents of file present in pull request on github. Is there any way to do this using pygithub?
For repository, we can do this using
contents = repo.get_contents(filename)
However, I did not find such api for pull request object. Is there any way to do this?
I have found a nice workaround to this. We can not get contents from File objects, however, it is possible to get them from repository, at any version. I did this like following :
This works irrespective of PR is open/closed/merged.
(1) Grab commits from PR.
(2) Grab files from commits.
(3) Use Repository object to get the contents of file at reference corresponding the sha of commit in PR.
Example :
github = Github(login_or_token=my_github_token)
repo = github.get_repo(repo_name, lazy=False)
# Grab PR
pull_number = 20
pr = repo.get_pull(pull_number)
commits = pr.get_commits()
for commit in commits:
files = commit.files
for file in files:
filename = file.filename
contents = repo.get_contents(filename, ref=commit.sha).decoded_content
# Now do whatever you want to do with contents :)
Take a look at this: https://docs.github.com/en/rest/reference/pulls#list-pull-requests-files
Have not tried, but pygithub does have a method for this called get_files to use this API call: https://pygithub.readthedocs.io/en/latest/github_objects/PullRequest.html#github.PullRequest.PullRequest.get_files
EDIT: Using requests:
import requests
username = 'astrochun'
repo = 'Zcalbase_gal'
pr = 84
response = requests.get(f"https://api.github.com/repos/{username}/{repo}/pulls/{pr}/files")
dict0 = response.json()
pr_files = [elem['filename'] for elem in dict0]

get all commits in a Git tag through GitHub API return only the first commit

I'm trying to get all commits in a git tag throw github api.
I checked in this stack overflow issue
but in this way, it asks me to compare between two tags and it returns me only the first commit. I want to get all commits of a specific tag.
what I used is
https://api.github.com/repos/:org/:repo/compare/:tag_1...:tag_2
because I want to specific tag I added the same tag
https://api.github.com/repos/xxxx/ant-design/compare/3.13.2...3.13.2
it return me the only 2 commits
but in the tag I have many commits as you can see here.
It wasn't clear what the constraints were tech-wise so here is an example solution using Node that should illustrate what's possible.
I believe the tags returned by /repos/:owner/:name/tags are not sorted by date created, but instead alphabetically, so I had to filter out the tags that didn't match the version regex to catch some stray inputs. To ensure the order was correct I used the semver npm package to sort them based on version.
Then it was a matter of just using the Compare endpoint with the two latest tags in the repository.
// API client for working with GitHub data using promises
const { Octokit } = require("#octokit/rest");
// helper function to compare two version strings using semver
const semverGt = require('semver/functions/gt');
const owner = "RapidAPI";
const repo = "ant-design";
const octokit = new Octokit();
octokit.repos
.listTags({
owner,
repo,
})
.then(({ data: tags }) => {
// filter out tags that don't look like releases
const sortedTaggedVersions = tags.filter(t => t.name.match(/\d+.\d+.\d+/))
.sort((a, b) => semverGt(a.name, b.name));
// these are out inputs for locating the commits that are in the latest
// release (aka "head") but are not in the previous release (aka "base")
const head = sortedTaggedVersions[0].name;
const base = sortedTaggedVersions[1].name;
console.log(`Comparing base ${base} and head ${head}...`)
return octokit.repos.compareCommits({
owner,
repo,
base,
head,
});
})
.then(({ data }) => {
console.log(`Found ${data.commits.length} commits:`);
for (const c of data.commits) {
let message = c.commit.message;
// only show first line of commit message to keep output clean
const newline = message.indexOf("\n");
if (newline > -1) {
message = message.substr(0, newline);
}
let author = c.author ? `#${c.author.login}` : null;
if (author == null) {
// use the name from the commit itself if we cannot find a GitHub committer
author = c.commit.author.name;
}
console.log(` - ${c.sha} - ${author} - ${message}`)
}
})
.catch(err => {
console.error("Unable to find commits", err);
});
This is the result:
$ node index.js
Comparing base 3.13.1 and head 3.13.2...
Found 19 commits:
- 4b526bf251fde5d4b6f1fec6d1ec3eb8805b4c75 - #orzyyyy - docs: fix wrong comma
- 736f5b9549a3de6d694786f63f835aa26c29d105 - #pine3ree - doc: handle invalid date in message.info() call
- 0d65f0578de652d2b3f5231088eaeaab95d8a3be - dependabot[bot] - :arrow_up: Update #types/react requirement from ~16.7.13 to ~16.8.1
- c895c809f91e7ce817d9a42c4e0fd3ea5311d198 - #gyh9457 - improve tabs demo (#14701)
- 163140189f57c225dd49758f4ea2b8116f201dc9 - #ashearer - Fix quote rendering (#14708)
- 31d55e43b358c148640a7991b444c56e1cf25456 - #ycjcl868 - upd: version
- 976a6a5c5a2adb3c407e953b95df08f6810e0cd5 - #Josephus-P - Fixed typos and improved grammar
- b6f81340baeec20caa8511693ea4ec7d7d0c0ba7 - #Josephus-P - small change
- 777c56a515159a2eb7e809695def53d66aebfc10 - #zombieJ - mock test
- 6f040b6c4090fbc060bf2a06a7a01b900f4fe890 - #ycjcl868 - fix: strict check input
- 6cdc203a2fc58b5c89ea7bfe0ef361e7afdf95e6 - #ycjcl868 - Merge pull request #14725 from ant-design/fix-form-test
- 99eeefc25d38a2e2060c23de0f8446fd90729911 - #imhele - correct type in Switch (#14727)
- 2b558af9600c0d0fa56467b8de0522b2a4277232 - #zombieJ - update silder snapshot (#14732)
- b3834e48b1e009adbd142a7e2c38a129729170de - #imhele - Table: fix showing only first page (#14724)
- 991b47f421bc3c60d30a8ff1d689615e6b70dbe1 - #zombieJ - update antd-tools version to check (#14738)
- dfc9b24c989c58ffe6a922b45286e09450f85579 - #GabeMedrash - Properly type onMouseEnter and onMouseLeave events
- 5ad97a33d1d65f05a121796210e4fa15f2894c5c - #afc163 - :lipstick: improve home page style in mobile device
- a9a6da47ed44d811e402822ec3933608405c27fb - #thilo-behnke - Input: Clear icon doesn't disappear if value is null (vs undefined or empy string) (#14733)
- dab30ef2ccead39135ff6e4b215259344d812897 - #zombieJ - update changelog (#14746)
This is different to the provided URL from the screenshot https://api.github.com/repos/RapidAPI/ant-design/compare/3.13.2...3.13.2 because it uses the version 3.13.2 for both tags.
yes, I would happy to know what is doing when the same tag..
To explain the /repos/:owner/:repo/compare/:base...:head API I first need to explain the corresponding API in Git itself - how to get a list of commits.
If you were to run git log 3.13.2 it would do a few things:
figure out that 3.13.2 is a tag (a branch, commit ID, or other aliases could also be used here)
find the commit that belongs to this tag
list the details about this first commit
check the first parent of this commit
GOTO 3
It'll then continue to traverse the history of the repository until you decide to exit the process.
But what if we only want to go back to a certain point? This is where "commit ranges" comes into play.
If you were working on the command line to find the commits between two tags, you'd probably use this syntax:
$ git log 3.13.1...3.13.2 --oneline
dab30ef2cc (tag: 3.13.2) update changelog (#14746)
a9a6da47ed Input: Clear icon doesn't disappear if value is null (vs undefined or empy string) (#14733)
5ad97a33d1 :lipstick: improve home page style in mobile device
dfc9b24c98 Properly type onMouseEnter and onMouseLeave events
991b47f421 update antd-tools version to check (#14738)
b3834e48b1 Table: fix showing only first page (#14724)
2b558af960 update silder snapshot (#14732)
99eeefc25d correct type in Switch (#14727)
6cdc203a2f Merge pull request #14725 from ant-design/fix-form-test
6f040b6c40 fix: strict check input
777c56a515 mock test
b6f81340ba small change
976a6a5c5a Fixed typos and improved grammar
31d55e43b3 upd: version
163140189f Fix quote rendering (#14708)
c895c809f9 improve tabs demo (#14701)
0d65f0578d :arrow_up: Update #types/react requirement from ~16.7.13 to ~16.8.1
736f5b9549 doc: handle invalid date in message.info() call
4b526bf251 docs: fix wrong comma
This is what the design of the /repos/:owner/:repo/compare/:base...:head API is based upon - providing the commit range that you want to query on in the form of two tags. But what happens when you provide the same tag twice?
$ git log 3.13.2...3.13.2 --oneline
No commits are returned, because Git thinks you want to find the range of commits from the same tag - which is an empty set. This is what the GitHub API is doing with your initial API call.
I want to point out a small bug in Brendan Forster's answer. I am new to stack overflow so can unfortunately not comment directly on his answer.
The sorting
tags. ...
.sort((a, b) => semverGt(a.name, b.name));
actually doesn't do anything. This is because semverGt returns a boolean value and JavaScript sort expects a comparator value (-1, 0 or 1).
A solution would be to manually translate to a comparator like:
tags. ...
.sort((a, b) => {
if (semverGt(a.name, b.name)) {return -1;}
if (semverGt(b.name, a.name)) {return 1;}
return 0;
});
The bug was quite hard to find because the results from GitHub are already somewhat ordered. Hope it saves someone from having the same headache as me!

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

How can I Diff a Svn Repository using SharpSvn

My question is quite simple and with the SharpSvn Api, it should be easy as well. Here what I did:
path = "c:\project";
using (SvnLookClient client = new SvnLookClient())
{
SvnLookOrigin o = new SvnLookOrigin(path);
Collection<SvnChangedEventArgs> changeList;
client.GetChanged(o, out changeList); // <-- Exception
}
and when I call the GetChanged, I get an exception:
Can't open file 'c:\project\format': The system cannot find the file specified.
So, Maybe there is something I'm missing? Or maybe it's not the right way to do find out the list of files and folders that were modified in the local repository?
Thanks in advance.
The SvnLookClient class in SharpSvn is the equivalent to the 'svnlook' console application. It is a low level tool that enables repository hooks to look into specific transactions of a repository using direct file access.
You probably want to use the SvnClient class to look at a WorkingCopy and most likely its Status() or in some cases simpler GetStatus() function to see what changed.
The path that the SvnLookOrigin constructor wants is actually:
path = "c:\project\.svn\";
That is, it wants that special ".svn" directory not just the root of where the source is checked out to.
Although you probably do want to listen to Bert and do something like:
path = "c:\project";
using (SvnLookClient client = new SvnLookClient())
{
SvnLookOrigin o = new SvnLookOrigin(path);
Collection<SvnChangedEventArgs> changeList;
client.GetStatus(o, out changeList); // Should now return the differences between this working copy and the remote status.
}