GitHub Commit Compare

Add controls to compare commits.

目前为 2024-02-04 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name GitHub Commit Compare
  3. // @namespace https://github.com/jerone/UserScripts
  4. // @description Add controls to compare commits.
  5. // @author jerone
  6. // @contributor darkred
  7. // @copyright 2017+, jerone (https://github.com/jerone)
  8. // @license CC-BY-NC-SA-4.0; https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode
  9. // @license GPL-3.0-or-later; http://www.gnu.org/licenses/gpl-3.0.txt
  10. // @homepage https://github.com/jerone/UserScripts/tree/master/GitHub_Commit_Compare
  11. // @homepageURL https://github.com/jerone/UserScripts/tree/master/GitHub_Commit_Compare
  12. // @supportURL https://github.com/jerone/UserScripts/issues
  13. // @contributionURL https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=VCYMHWQ7ZMBKW
  14. // @icon https://github.githubassets.com/pinned-octocat.svg
  15. // @include https://github.com/*/*/commits
  16. // @include https://github.com/*/*/commits/*
  17. // @exclude https://github.com/*/*.diff
  18. // @exclude https://github.com/*/*.patch
  19. // @version 0.0.3
  20. // @grant none
  21. // ==/UserScript==
  22.  
  23. // cSpell:ignore tooltipped, Consolas, Menlo, compareAdisabled, compareBdisabled
  24.  
  25. (function () {
  26. function addButton() {
  27. var nav;
  28. if ((nav = document.querySelector(".file-navigation"))) {
  29. // Check if our group of buttons are already attached.
  30. // Remove it, as the 'old' buttons don't have events anymore.
  31. const e = document.getElementById("GitHubCommitCompareGroup");
  32. if (e) {
  33. e.parentElement.removeChild(e);
  34. }
  35. Array.from(
  36. document.querySelectorAll(".GitHubCommitCompareButtonAB"),
  37. ).forEach(function (b) {
  38. b.parentElement.removeChild(b);
  39. });
  40.  
  41. const c = document.createElement("input");
  42. c.type = "checkbox";
  43. c.addEventListener("change", function () {
  44. const bb = document.querySelectorAll(
  45. ".GitHubCommitCompareButtonAB",
  46. );
  47. if (this.checked) {
  48. if (bb.length === 0) {
  49. addCompareButtons();
  50. } else {
  51. Array.from(bb).forEach(function (b) {
  52. b.classList.remove("d-none");
  53. });
  54. }
  55. } else {
  56. Array.from(bb).forEach(function (b) {
  57. b.classList.add("d-none");
  58. });
  59. }
  60. const bbb = document.getElementById(
  61. "GitHubCommitCompareButton",
  62. );
  63. if (bbb) bbb.classList.remove("disabled");
  64. });
  65.  
  66. const l = document.createElement("label");
  67. l.classList.add("tooltipped", "tooltipped-n");
  68. l.setAttribute("aria-label", "Show commit compare buttons");
  69. l.style.cssText = `
  70. float: left;
  71. padding: 3px 10px;
  72. font-size: 12px;
  73. font-weight: 600;
  74. line-height: 20px;
  75. color: #24292e;
  76. vertical-align: middle;
  77. background-color: #fff;
  78. border: 1px solid rgba(27,31,35,0.2);
  79. border-right: 0;
  80. border-top-left-radius: 3px;
  81. border-bottom-left-radius: 3px;
  82. `;
  83. l.appendChild(c);
  84.  
  85. const p = document.createElementNS(
  86. "http://www.w3.org/2000/svg",
  87. "path",
  88. );
  89. p.setAttributeNS(
  90. null,
  91. "d",
  92. "M5 12H4c-.27-.02-.48-.11-.69-.31-.21-.2-.3-.42-.31-.69V4.72A1.993 1.993 0 0 0 2 1a1.993 1.993 0 0 0-1 3.72V11c.03.78.34 1.47.94 2.06.6.59 1.28.91 2.06.94h1v2l3-3-3-3v2zM2 1.8c.66 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2C1.35 4.2.8 3.65.8 3c0-.65.55-1.2 1.2-1.2zm11 9.48V5c-.03-.78-.34-1.47-.94-2.06-.6-.59-1.28-.91-2.06-.94H9V0L6 3l3 3V4h1c.27.02.48.11.69.31.21.2.3.42.31.69v6.28A1.993 1.993 0 0 0 12 15a1.993 1.993 0 0 0 1-3.72zm-1 2.92c-.66 0-1.2-.55-1.2-1.2 0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2z",
  93. );
  94.  
  95. const s = document.createElementNS(
  96. "http://www.w3.org/2000/svg",
  97. "svg",
  98. );
  99. s.classList.add("octicon", "octicon-diff");
  100. s.setAttributeNS(null, "height", 16);
  101. s.setAttributeNS(null, "width", 14);
  102. s.setAttributeNS(null, "viewBox", "0 0 14 16");
  103. s.appendChild(p);
  104.  
  105. const a = document.createElement("a");
  106. a.id = "GitHubCommitCompareButton";
  107. a.classList.add(
  108. "btn",
  109. "btn-sm",
  110. "tooltipped",
  111. "tooltipped-n",
  112. "disabled",
  113. );
  114. a.setAttribute("href", "#");
  115. a.setAttribute("rel", "nofollow");
  116. a.setAttribute("aria-label", "Compare these commits");
  117. a.style.cssText = `
  118. border-top-left-radius: 0;
  119. border-bottom-left-radius: 0;
  120. font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
  121. `;
  122. a.appendChild(s);
  123. a.appendChild(document.createElement("span"));
  124.  
  125. const g = document.createElement("div");
  126. g.id = "GitHubCommitCompareGroup";
  127. g.classList.add("float-right");
  128. g.appendChild(l);
  129. g.appendChild(a);
  130.  
  131. nav.appendChild(g);
  132. }
  133. }
  134.  
  135. function updateRadioButtons() {
  136. var compareAdisabled = true;
  137. var compareBdisabled = false;
  138.  
  139. const compares = document.querySelectorAll(
  140. ".GitHubCommitCompareButtonAB",
  141. );
  142. Array.from(compares).forEach(function (compare) {
  143. const compareA = compare.querySelector(
  144. '[name="GitHubCommitCompareButtonA"]',
  145. );
  146. const compareB = compare.querySelector(
  147. '[name="GitHubCommitCompareButtonB"]',
  148. );
  149.  
  150. compareA.disabled = compareAdisabled;
  151. compareA.parentNode.classList.toggle("disabled", compareAdisabled);
  152.  
  153. if (compareA.checked) {
  154. compareBdisabled = true;
  155. }
  156. if (compareB.checked) {
  157. compareAdisabled = false;
  158. }
  159.  
  160. compareB.disabled = compareBdisabled;
  161. compareB.parentNode.classList.toggle("disabled", compareBdisabled);
  162. });
  163.  
  164. updateCompareButton();
  165. }
  166.  
  167. function updateCompareButton() {
  168. const repo = document.querySelector('meta[property="og:url"]').content;
  169.  
  170. const compareA = document.querySelector(
  171. '.GitHubCommitCompareButtonAB [name="GitHubCommitCompareButtonA"]:checked',
  172. );
  173. const hashA =
  174. compareA.parentNode.parentNode.parentNode.querySelector(
  175. "clipboard-copy",
  176. ).value;
  177. const compareB = document.querySelector(
  178. '.GitHubCommitCompareButtonAB [name="GitHubCommitCompareButtonB"]:checked',
  179. );
  180. const hashB =
  181. compareB.parentNode.parentNode.parentNode.querySelector(
  182. "clipboard-copy",
  183. ).value;
  184.  
  185. const a = document.getElementById("GitHubCommitCompareButton");
  186. a.setAttribute("href", `${repo}/compare/${hashA}...${hashB}`);
  187. a.querySelector("span").textContent =
  188. ` ${hashA.substring(0, 7)}...${hashB.substring(0, 7)}`;
  189.  
  190. //localStorage.setItem('GitHubCommitCompareButtonHashA', hashA);
  191. //localStorage.setItem('GitHubCommitCompareButtonHashB', hashB);
  192. }
  193.  
  194. function addCompareButtons() {
  195. const commits = document.querySelectorAll(
  196. ".commits-list-item .commit-links-cell",
  197. );
  198. Array.from(commits).forEach(function (item, index) {
  199. const c1 = document.createElement("input");
  200. c1.name = "GitHubCommitCompareButtonA";
  201. c1.type = "radio";
  202. c1.addEventListener("change", updateRadioButtons);
  203. if (index === 1) c1.checked = true;
  204.  
  205. const l1 = document.createElement("label");
  206. l1.classList.add(
  207. "btn",
  208. "btn-outline",
  209. "BtnGroup-item",
  210. "tooltipped",
  211. "tooltipped-s",
  212. );
  213. l1.setAttribute("aria-label", "Choose a base commit");
  214. l1.appendChild(c1);
  215.  
  216. const c2 = document.createElement("input");
  217. c2.name = "GitHubCommitCompareButtonB";
  218. c2.type = "radio";
  219. c2.addEventListener("change", updateRadioButtons);
  220. if (index === 0) c2.checked = true;
  221.  
  222. const l2 = document.createElement("label");
  223. l2.classList.add(
  224. "btn",
  225. "btn-outline",
  226. "BtnGroup-item",
  227. "tooltipped",
  228. "tooltipped-s",
  229. );
  230. l2.setAttribute("aria-label", "Choose a head commit");
  231. l2.appendChild(c2);
  232.  
  233. // const p3 = document.createElementNS('http://www.w3.org/2000/svg', 'path');
  234. // p3.setAttributeNS(null,
  235. //'d',
  236. //'M5 12H4c-.27-.02-.48-.11-.69-.31-.21-.2-.3-.42-.31-.69V4.72A1.993 1.993 0 0 0 2 1a1.993 1.993 0 0 0-1 3.72V11c.03.78.34 1.47.94 2.06.6.59 1.28.91 2.06.94h1v2l3-3-3-3v2zM2 1.8c.66 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2C1.35 4.2.8 3.65.8 3c0-.65.55-1.2 1.2-1.2zm11 9.48V5c-.03-.78-.34-1.47-.94-2.06-.6-.59-1.28-.91-2.06-.94H9V0L6 3l3 3V4h1c.27.02.48.11.69.31.21.2.3.42.31.69v6.28A1.993 1.993 0 0 0 12 15a1.993 1.993 0 0 0 1-3.72zm-1 2.92c-.66 0-1.2-.55-1.2-1.2 0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2z');
  237.  
  238. // const s3 = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
  239. // s3.classList.add('octicon', 'octicon-diff');
  240. // s3.setAttributeNS(null, 'height', 16);
  241. // s3.setAttributeNS(null, 'width', 14);
  242. // s3.setAttributeNS(null, 'viewBox', '0 0 14 16');
  243. // s3.appendChild(p3);
  244.  
  245. // const l3 = document.createElement('a');
  246. // l3.classList.add('btn', 'btn-outline', 'BtnGroup-item', 'tooltipped', 'tooltipped-sw');
  247. // l3.setAttribute('aria-label', 'TODO');
  248. // l3.appendChild(s3);
  249.  
  250. const gg = document.createElement("div");
  251. gg.classList.add(
  252. "GitHubCommitCompareButtonAB",
  253. "commit-links-group",
  254. "BtnGroup",
  255. );
  256. gg.appendChild(l1);
  257. gg.appendChild(l2);
  258. //gg.appendChild(l3);
  259.  
  260. //item.style.width = '350px';
  261. if (item.querySelector(".muted-link")) {
  262. // Insert after number of comments button.
  263. item.insertBefore(
  264. gg,
  265. item.querySelector(".muted-link").nextSibling,
  266. );
  267. } else {
  268. item.insertBefore(gg, item.firstChild);
  269. }
  270. });
  271.  
  272. updateRadioButtons(); // Update radio buttons.
  273. }
  274.  
  275. // Init.
  276. addButton();
  277.  
  278. // Pjax.
  279. document.addEventListener("pjax:end", addButton);
  280. })();