GitHub First Commit

Add a link to a GitHub repo's first commit

As of 2018-02-17. See the latest version.

  1. // ==UserScript==
  2. // @name GitHub First Commit
  3. // @description Add a link to a GitHub repo's first commit
  4. // @author chocolateboy
  5. // @copyright chocolateboy
  6. // @namespace https://github.com/chocolateboy/userscripts
  7. // @version 2.2.0
  8. // @license GPL: http://www.gnu.org/copyleft/gpl.html
  9. // @include https://github.com/*/*
  10. // @require https://code.jquery.com/jquery-3.3.1.min.js
  11. // @require https://cdn.rawgit.com/eclecto/jQuery-onMutate/79bbb2b8caccabfc9b9ade046fe63f15f593fef6/src/jquery.onmutate.min.js
  12. // @grant GM_log
  13. // ==/UserScript==
  14.  
  15. // XXX note: the unused grant is a workaround for a Greasemonkey bug:
  16. // https://github.com/greasemonkey/greasemonkey/issues/1614
  17.  
  18. const COMMIT_BAR = 'div.commit-tease.js-details-container > span.float-right'
  19.  
  20. const FIRST_COMMIT =
  21. `<span id="first-commit">
  22. |&nbsp;
  23. <a id="first-commit-link" style="cursor: pointer" class="message">First commit</a>
  24. </span>`
  25.  
  26. // this function extracts (and navigates to) the URL of the repo's first-commit.
  27. // it is based on:
  28. //
  29. // https://gist.github.com/pitaj/e52862409dd5726711214a55189f332d
  30. //
  31. // similar/related snippets are listed here:
  32. //
  33. // https://github.com/wong2/first-commit/issues/15#issuecomment-317750579
  34. function openFirstCommit (user, repo) {
  35. return fetch(`https://api.github.com/repos/${user}/${repo}/commits`)
  36. // the `Link` header has additional URLs for paging.
  37. // parse the original JSON for the case where no other pages exist
  38. .then(res => Promise.all([res.headers.get('link'), res.json()]))
  39.  
  40. .then(([link, commits]) => {
  41. if (link) {
  42. // the link header contains two URLs and has the following
  43. // format in a single line (wrapped for readability):
  44. //
  45. // <https://api.github.com/repositories/1234/commits?page=2>;
  46. // rel="next",
  47. // <https://api.github.com/repositories/1234/commits?page=9>;
  48. // rel="last"
  49.  
  50. // extract the URL of the last page
  51. const lastPage = link.match(/^.+?<([^>]+)>;/)[1]
  52.  
  53. // fetch the last page of results
  54. return fetch(lastPage).then(res => res.json())
  55. }
  56.  
  57. // if no link, we know we're on the only page
  58. return commits
  59. })
  60.  
  61. // get the last commit and extract the target URL
  62. .then(commits => commits[commits.length - 1].html_url)
  63.  
  64. // navigate there
  65. .then(url => location.href = url)
  66. }
  67.  
  68. // add the "First commit" link as the last child of the commit bar
  69. function addLink ($commitBar) {
  70. const [user, repo] = $('meta[name="octolytics-dimension-repository_network_root_nwo"]')
  71. .attr('content')
  72. .split('/')
  73.  
  74. // the "First commit" link already exists when navigating to a repo's
  75. // homepage via the back button. however, resurrecting the link in this way
  76. // causes its onclick event handler to be unregistered (XXX why?), so we
  77. // need to re-attach it
  78. let $firstCommit = $commitBar.find('#first-commit')
  79.  
  80. if (!$firstCommit.length) {
  81. $firstCommit = $(FIRST_COMMIT)
  82. $commitBar.append($firstCommit)
  83. }
  84.  
  85. const $link = $firstCommit.find('#first-commit-link')
  86.  
  87. $link.on('click', event => {
  88. $link.text('Loading...')
  89. openFirstCommit(user, repo)
  90. return false
  91. })
  92. }
  93.  
  94. // the commit bar (div.commit-tease) is statically defined in the HTML
  95. // for users who aren't logged in. for logged in users, it's loaded dynamically
  96. // via an <include-fragment> custom element:
  97. //
  98. // https://github.com/github/include-fragment-element
  99. //
  100. // jQuery-onMutate fires the callback immediately if the element already exists,
  101. // so it handles both cases
  102.  
  103. // #js-repo-pjax-container is only created on repo homepages
  104. // see here for more details: https://github.com/Mottie/GitHub-userscripts/wiki/How-to
  105. $('#js-repo-pjax-container').onCreate(COMMIT_BAR, addLink, true /* multi */)