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

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!

Related

How do I get Swashbuckle to have Swagger UI to group by Version?

I am playing with the new Azure API Apps (template in Visual Studio 2013 w/ the new SDK bits from 3/24/15) and I'd like have my Swagger UI group my calls by Version #. In my case, I'm currently versioning by URI (I realize REST purists will tell me not to do this - please don't try to "correct my error" here). For instance, I may have these calls:
http://example.com/api/Contacts <-- "latest"
http://example.com/api/1/Contacts
http://example.com/api/2/Contacts
http://example.com/api/Contacts{id} <-- "latest"
http://example.com/api/1/Contacts/{id}
http://example.com/api/2/Contacts/{id}
Functionally, this works great! (Yes, I know some of you will cringe. Sorry this hurts your feelings.) However, my problem is w/ Swagger UI organization. By default, Swagger UI groups these by the Controller Name (Contacts in this case). I see in the SwaggerConfig.cs file that I can change this:
// Each operation be assigned one or more tags which are then used by consumers for various reasons.
// For example, the swagger-ui groups operations according to the first tag of each operation.
// By default, this will be controller name but you can use the "GroupActionsBy" option to
// override with any value.
//
//c.GroupActionsBy(apiDesc => apiDesc.HttpMethod.ToString());
What I don't understand is how I can tweak this to group all of the "latest" together and then all of v1 together and then all of v2 together, etc.
How can I do this? If it absolutely must require that I add the word "latest" (or equiv) into the path in place of the version number, then I can do that but I'd prefer not have to do that.
I believe what you're looking to do is to uncomment a line a few lines below that one in SwaggerConfig.cs
c.OrderActionGroupsBy(new DescendingAlphabeticComparer());
except you'd change the name of the class to something like ApiVersionComparer() and then implement it as a new class:
public class ApiVersionComparer : IComparer<string>
{
public int Compare(string x, string y)
{
// Write whatever comparer you'd like to here.
// Yours would likely involve parsing the strings and having
// more complex logic than this....
return -(string.Compare(x, y));
}
}
If you've gotten far enough to ask this question, I'm sure I can leave the sort implementation for you. :-)

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

How can I grab the latest "stable" repo version from the Github API?

Github's tag method returns a list of all tags pushed to your repo with the latest tag listed at the top. Here's an example call: https://api.github.com/repos/ff0000/rosy/tags which produces the following json object.
[{
name: "rbp-folder-rename",
zipball_url: "https://api.github.com/repos/ff0000/rosy/zipball/rbp-folder-rename",
tarball_url: "https://api.github.com/repos/ff0000/rosy/tarball/rbp-folder-rename",
commit: {
sha: "09ebda2678d932a005fc86ab78f6c04eebdcd50d",
url: "https://api.github.com/repos/ff0000/rosy/commits/09ebda2678d932a005fc86ab78f6c04eebdcd50d"
}
},
{
name: "2.0.10",
zipball_url: "https://api.github.com/repos/ff0000/rosy/zipball/2.0.10",
tarball_url: "https://api.github.com/repos/ff0000/rosy/tarball/2.0.10",
commit: {
sha: "fe284c7d461107d9d08d2d4dcb676759f9485fc1",
url: "https://api.github.com/repos/ff0000/rosy/commits/fe284c7d461107d9d08d2d4dcb676759f9485fc1"
}
},
// ....
{
name: "2.1.5",
zipball_url: "https://api.github.com/repos/ff0000/rosy/zipball/2.1.5",
tarball_url: "https://api.github.com/repos/ff0000/rosy/tarball/2.1.5",
commit: {
sha: "db92baa49657b3c3d27b1475c415c19525cb2118",
url: "https://api.github.com/repos/ff0000/rosy/commits/db92baa49657b3c3d27b1475c415c19525cb2118"
}
}]
Questions
This list appears to have the latest tag at the top, followed by a history of previous tags listed in reverse chronological order. Why? That seems odd that the first result is ordered differently then the rest, maybe I'm reading this wrong?
Is there any way to programmatically retrieve the latest version applied to the master branch only? I'd like to programmatically retrieve the latest stable version of a repo.
Any help / insight would be appreciated.
To get the latest version number:
https://api.github.com/repos/user/repo/releases/latest
example, for this repo (https://github.com/pyIDM/PyIDM) you can use below url:
https://api.github.com/repos/pyidm/pyidm/releases/latest
you will get a json file with tag_name=latest version
This list appears to have the latest tag at the top, followed by a history of previous tags listed in reverse chronological order.
You shouldn't depend on the order in which the GitHub API returns tag, because there are no timestamps, and the repo contributors might have used inconsistent tag names, e.g. v1.9.0 and 2.5.14. In that particular example, v1.9.0 will show up first - see this repo.
You should bug the maintainers to use consistent tags (example), and sort GitHub's output anyway according to semver rules. Since these rules are non-trivial (see point 11 at that link), it's better to use the semver library (ported for the browser).
var gitHubPath = 'ff0000/rosy'; // your example repo
var url = 'https://api.github.com/repos/' + gitHubPath + '/tags';
$.get(url).done(function (data) {
var versions = data.sort(function (v1, v2) {
return semver.compare(v2.name, v1.name)
});
$('#result').html(versions[0].name);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://rawgit.com/hippich/bower-semver/master/semver.min.js"></script>
<p>Latest tag: <span id="result"></span></p>
Getting the latest "stable" release
GitHub releases have a prerelease flag, which can be true or false. If you define "stable" as prerelease: false, then you can fetch the releases, filter for prerelease: false and sort.
var gitHubPath = 'idorecall/selection-menu'; // your example repo doesn't have releases
var url = 'https://api.github.com/repos/' + gitHubPath + '/releases';
$.get(url).done(function (data) {
var releases = data.filter(function (release) {
return !release.prerelease;
})
releases = releases.sort(function (v1, v2) {
return Date.parse(v2.published_at) - Date.parse(v1.published_at);
});
console.log(releases[0]);
$('#result').html(releases[0].name);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>Latest release name: <span id="result"></span></p>
You could simply replace the tag by the branch name:
https://api.github.com/repos/ff0000/rosy/zipball/master
See the more general form of that query (for a given branch) in "Is there anyway to programmatically fetch a zipball of private github repo?".
But that presume that the latest stable version of a repo is in master (it could be the latest 'development', which might not be very stable past the fact that it compile and passes basic unit test): each project can have its own convention.
For what it's worth, https://api.github.com/repos/ff0000/rosy/branches would list the branches for that same repo.

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