Skip to content

Instantly share code, notes, and snippets.

@tony-sol
Created February 13, 2025 11:30
Show Gist options
  • Save tony-sol/2a5c004dd64e8ac54d6eb250e4dfa70d to your computer and use it in GitHub Desktop.
Save tony-sol/2a5c004dd64e8ac54d6eb250e4dfa70d to your computer and use it in GitHub Desktop.

Revisions

  1. tony-sol created this gist Feb 13, 2025.
    65 changes: 65 additions & 0 deletions getForksStats.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,65 @@
    javascript:(async () => {
    /* Ensure the script is run on a valid GitHub repository page */
    const ensureForksPage = () => {
    const match = window.location.href.match(/^https:\/\/github\.com\/([^/]+)\/([^/]+)(\/forks\/?)?/);
    if (!match) {
    alert('Run this from a GitHub repository page.');
    throw new Error('Not on a valid GitHub repository page.');
    }
    if (!match[3]) {
    window.location.href = `https://github.com/${match[1]}/${match[2]}/forks`;
    throw new Error('Redirecting to the forks page.');
    }
    return { user: match[1], repo: match[2] };
    };

    /* Fetch the default branch of a repository */
    const getDefaultBranch = async (user, repo) => {
    let token = prompt("Insert your GITHUB_TOKEN. It won't be saved");
    const res = await fetch(`https://api.github.com/repos/${user}/${repo}`, { headers: { accept: 'application/json', authorization: `Bearer ${token}` } });
    if (!res.ok) throw new Error(`Failed to fetch default branch for ${user}/${repo}`);
    return (await res.json()).default_branch;
    };

    /* Fetch branch information */
    const fetchBranchInfo = async (user, repo, branch) => {
    try {
    const res = await fetch(`https://github.com/${user}/${repo}/branch-infobar/${branch}`, { headers: { accept: 'application/json' } });
    if (res.ok) return (await res.json()).refComparison;
    } catch (e) {
    console.error(`Error fetching branch info for ${user}/${repo}:`, e);
    }
    return null;
    };

    try {
    /* Ensure on the forks page and get main repository details */
    const { user: mainUser, repo: mainRepo } = ensureForksPage();
    const defaultBranch = await getDefaultBranch(mainUser, mainRepo);

    /* Process fork links */
    const forks = [...document.querySelectorAll('ul[data-view-component="true"] > li')];
    for (const fork of forks) {
    const repoLink = fork.querySelector('a[href*="/"]:nth-of-type(2)');
    const match = repoLink?.href.match(/github\.com\/([^/]+)\/([^/]+)/);
    if (!match) continue;

    const [_, user, repo] = match;
    let branchInfo = await fetchBranchInfo(user, repo, defaultBranch);
    if (!branchInfo) branchInfo = await fetchBranchInfo(user, repo, await getDefaultBranch(user, repo));

    if (branchInfo) {
    const { ahead, behind } = branchInfo;
    const info = `Ahead: <font color="#0c0">${ahead}</font>, Behind: <font color="red">${behind}</font>`;
    const infoDiv = document.createElement('div');
    infoDiv.innerHTML = info;
    infoDiv.style.marginTop = '10px';
    infoDiv.style.fontSize = 'small';
    fork.appendChild(infoDiv);
    if (ahead === 0) fork.style.display = 'none';
    }
    }
    } catch (e) {
    console.error('Error in bookmarklet execution:', e);
    }
    })();