Skip to content

Instantly share code, notes, and snippets.

@bitbybit
Created May 24, 2026 14:24
Show Gist options
  • Select an option

  • Save bitbybit/a6019a37364b2a9c69095b8b334fca89 to your computer and use it in GitHub Desktop.

Select an option

Save bitbybit/a6019a37364b2a9c69095b8b334fca89 to your computer and use it in GitHub Desktop.
Removing yourself from every repository where you are a collaborator on GitHub
// Run this in the browser console on the https://github.com/settings/repositories page
(async function massLeaveRepos() {
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
// Helper 1: Waits for element to exist AND enter the viewport
const waitForVisible = (selector) => {
return new Promise((resolve) => {
// First, wait for the element to actually exist in the DOM
const checkExist = setInterval(() => {
const el = document.querySelector(selector);
if (el && !el.disabled) {
clearInterval(checkExist);
// Once it exists, observe it to see when it paints on screen
const observer = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting) {
observer.disconnect(); // Stop observing once found
resolve(el);
}
});
observer.observe(el);
}
}, 100);
});
};
// Helper 2: Waits for element to leave the viewport or be destroyed
const waitForHidden = (selector) => {
return new Promise((resolve) => {
const el = document.querySelector(selector);
if (!el) return resolve(); // Already gone
let isResolved = false;
const observer = new IntersectionObserver((entries) => {
if (!entries[0].isIntersecting) {
observer.disconnect();
if (!isResolved) {
isResolved = true;
resolve();
}
}
});
observer.observe(el);
// Backup: IntersectionObserver can sometimes be flaky if an element
// is ripped completely out of the DOM rather than just hidden.
const checkRemoved = setInterval(() => {
if (!document.querySelector(selector)) {
observer.disconnect();
clearInterval(checkRemoved);
if (!isResolved) {
isResolved = true;
resolve();
}
}
}, 100);
});
};
const leaveButtons = Array.from(document.querySelectorAll('[data-repo-id] [role="button"]'));
if (leaveButtons.length === 0) {
console.log("No repositories found to leave.");
return;
}
console.log(`Found ${leaveButtons.length} repositories. Starting process...`);
for (let i = 0; i < leaveButtons.length; i++) {
console.log(`Processing repo ${i + 1} of ${leaveButtons.length}...`);
// 1. Click initial button
leaveButtons[i].click();
// 2. Wait for the dialog button to enter the viewport
const confirmBtn = await waitForVisible('.js-leave-collaborated-repo .btn');
// 3. Click the confirmation
confirmBtn.click();
// 4. Wait for the dialog button to leave the viewport
await waitForHidden('.js-leave-collaborated-repo .btn');
// 5. Safety delay before the next iteration
await sleep(600);
}
console.log("Finished leaving all repositories on this page!");
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment