{"id":"l-git-3-4","courseId":"git-github","lang":"en","kind":"lesson","summary":"Plan work with issues, labels and a GitHub Project board, then contribute to a repository you don’t own by forking it, tracking upstream, and opening a pull request from your fork.","html":"<p>The portfolio is on GitHub and changes reach <code dir=\"ltr\">main</code> through pull requests. Two questions remain. How do you keep track of what to do next, without a sticky note on your monitor? And how do you contribute to a project you don’t own, where you can’t even push a branch? GitHub answers the first with issues and Projects, and the second with forks.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">Issues: work you can link to</h2>\n<p>An <strong>issue</strong> is a tracked piece of work: a bug, a feature idea, a question. Each gets a number shared with pull requests (<code dir=\"ltr\">#12</code>), a discussion thread, and metadata:</p>\n<ul>\n<li><strong>Labels</strong> categorise it: <code dir=\"ltr\">bug</code>, <code dir=\"ltr\">enhancement</code>, <code dir=\"ltr\">documentation</code>, <code dir=\"ltr\">good first issue</code>.</li>\n<li><strong>Assignees</strong> say who’s on it.</li>\n<li><strong>Milestones</strong> group issues toward a goal, such as “Launch v1”.</li>\n</ul>\n<p>For your portfolio, write the to-do list as issues: “Projects page: filter by tag (#12)”, “Footer overlaps content on mobile (#13)”. Then each pull request names its issue with <code dir=\"ltr\">Closes #12</code>, merging closes it, and six months later anyone can follow the trail from a line of code to the commit, the pull request, the discussion and the original reason.</p>\n<p>A good bug report has steps to reproduce, what you expected, what happened, and the browser or version. Repositories can add issue templates in <code dir=\"ltr\">.github/ISSUE_TEMPLATE/</code> so reporters fill those sections in automatically.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">Projects: the board above the issues</h2>\n<p>A <strong>GitHub Project</strong> is a planning view over issues and pull requests, possibly from several repositories. It starts as a spreadsheet-like table; you add custom fields such as Status, Priority, Size or Iteration, and switch between <strong>table</strong>, <strong>board</strong> (columns by Status, the familiar kanban) and <strong>roadmap</strong> (items on a timeline) layouts. Built-in workflows can move items automatically, for example to Done when an issue closes or a pull request merges.</p>\n<p>For a solo portfolio, a board with Todo, In progress and Done is plenty. On a team, the Project is where planning conversations happen and the issues hold the detail.</p>\n<h2 id=\"sec-3\" tabindex=\"-1\">Forks: contributing without write access</h2>\n<p>Say you’ve found a typo in the docs of an open-source theme library, <code dir=\"ltr\">open-themes/vite-themes</code>. You can clone it, but you can’t push to it: you’re not a collaborator, and <code dir=\"ltr\">git push</code> fails with a permission error. That’s what forks solve.</p>\n<p>A <strong>fork</strong> is your own copy of the repository on GitHub, under your account: <code dir=\"ltr\">maya-okafor/vite-themes</code>. You can push anything to it. When your change is ready, you open a pull request from a branch on your fork to the original repository, which by convention is called <strong>upstream</strong>.</p>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 700 290\" role=\"img\" aria-labelledby=\"l-git-3-4-t1\"><defs><marker id=\"arrow-l-git-3-4-1\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto-start-reverse\"><path d=\"M0 0 L10 5 L0 10 z\" class=\"d-arrowhead\"/></marker></defs>\n  <title id=\"l-git-3-4-t1\">Three repositories in a triangle. Top left: upstream, open-themes/vite-themes on GitHub. Top right: origin, your fork maya-okafor/vite-themes on GitHub. Bottom: your local clone. The fork button copies upstream to origin. Your clone fetches from upstream and pushes branches to origin. A pull request goes from origin to upstream.</title>\n  <rect class=\"d-box-primary\" x=\"20\" y=\"20\" width=\"250\" height=\"70\" rx=\"12\"/>\n  <text class=\"d-label-strong\" x=\"145\" y=\"48\" text-anchor=\"middle\">upstream</text>\n  <text class=\"d-code\" x=\"145\" y=\"72\" text-anchor=\"middle\">open-themes/vite-themes</text>\n  <rect class=\"d-box-accent\" x=\"430\" y=\"20\" width=\"250\" height=\"70\" rx=\"12\"/>\n  <text class=\"d-label-strong\" x=\"555\" y=\"48\" text-anchor=\"middle\">origin (your fork)</text>\n  <text class=\"d-code\" x=\"555\" y=\"72\" text-anchor=\"middle\">maya-okafor/vite-themes</text>\n  <rect class=\"d-box\" x=\"225\" y=\"200\" width=\"250\" height=\"70\" rx=\"12\"/>\n  <text class=\"d-label-strong\" x=\"350\" y=\"228\" text-anchor=\"middle\">Your local clone</text>\n  <text class=\"d-code\" x=\"350\" y=\"252\" text-anchor=\"middle\">~/code/vite-themes</text>\n  <path class=\"d-arrow\" d=\"M270 40 L426 40\" marker-end=\"url(#arrow-l-git-3-4-1)\"/>\n  <text class=\"d-label-muted\" x=\"348\" y=\"32\" text-anchor=\"middle\">Fork</text>\n  <path class=\"d-arrow d-dashed\" d=\"M430 75 L274 75\" marker-end=\"url(#arrow-l-git-3-4-1)\"/>\n  <text class=\"d-label-muted\" x=\"352\" y=\"100\" text-anchor=\"middle\">pull request</text>\n  <path class=\"d-arrow\" d=\"M120 92 L250 196\" marker-end=\"url(#arrow-l-git-3-4-1)\"/>\n  <text class=\"d-code\" x=\"30\" y=\"160\">git fetch upstream</text>\n  <path class=\"d-arrow\" d=\"M450 196 L560 94\" marker-end=\"url(#arrow-l-git-3-4-1)\"/>\n  <text class=\"d-code\" x=\"520\" y=\"160\">git push origin</text>\n</svg><figcaption>The fork workflow: fetch from upstream, push to origin, pull request back</figcaption></figure>\n<p>Here’s the whole routine:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">Terminal</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#6E7781;--shiki-dark:#8B949E\"># 1. Click Fork on github.com/open-themes/vite-themes, then clone YOUR fork</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> clone</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> git@github.com:maya-okafor/vite-themes.git</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">cd</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> vite-themes</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#6E7781;--shiki-dark:#8B949E\"># 2. Remember where the original lives</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> remote</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> add</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> upstream</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> https://github.com/open-themes/vite-themes.git</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> remote</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -v</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">    # origin = your fork, upstream = the original</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#6E7781;--shiki-dark:#8B949E\"># 3. Work on a branch, never on main</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> switch</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -c</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> fix-readme-typo</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> commit</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -am</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"Fix typo in installation steps\"</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> push</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -u</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> origin</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> fix-readme-typo</span></span></code></pre></div>\n<p>GitHub then offers “Compare &amp; pull request” on your fork. Check that the base repository is <code dir=\"ltr\">open-themes/vite-themes</code> and the base branch is their <code dir=\"ltr\">main</code>. The CLI shortcut for steps 1 and 2 is <code dir=\"ltr\">gh repo fork open-themes/vite-themes --clone</code>, which also sets up the <code dir=\"ltr\">upstream</code> remote for you.</p>\n<p>Leave “Allow edits by maintainers” ticked when you open the pull request. It lets maintainers push small fixes to your branch instead of asking you for every tweak.</p>\n<h2 id=\"sec-4\" tabindex=\"-1\">Keeping your fork current</h2>\n<p>The original keeps moving. Bring its changes into your fork before starting each new branch:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">Terminal</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> fetch</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> upstream</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> switch</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> main</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> merge</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --ff-only</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> upstream/main</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> push</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> origin</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> main</span></span></code></pre></div>\n<p>Because you never commit on your fork’s <code dir=\"ltr\">main</code>, this is always a fast-forward. The <strong>Sync fork</strong> button on your fork’s GitHub page, or <code dir=\"ltr\">gh repo sync maya-okafor/vite-themes</code>, updates the GitHub copy; you still pull it locally. (Plain <code dir=\"ltr\">gh repo sync</code> with no argument updates your local clone instead.)</p>\n<aside class=\"callout callout-mistake\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg><span><span class=\"callout-kind\">Common mistake</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>Working on your fork’s main</span></p>\n<p>If you commit to <code dir=\"ltr\">main</code> on your fork, your pull request is tied to <code dir=\"ltr\">main</code>, every later commit joins it, and syncing with upstream stops being a fast-forward. Keep <code dir=\"ltr\">main</code> as a clean mirror of upstream and give each contribution its own branch.</p>\n</aside>\n<h2 id=\"sec-5\" tabindex=\"-1\">Being a welcome contributor</h2>\n<p>Before writing code, read the project’s <strong>README</strong> and <strong>CONTRIBUTING.md</strong> (setup, tests, style, whether an issue is required first) and its code of conduct. Search existing issues and pull requests; your bug may already be known or fixed. For anything bigger than a typo, open or comment on an issue and agree on the approach before writing code. Issues labelled <code dir=\"ltr\">good first issue</code> are deliberately picked as starting points.</p>\n<p>Then keep the pull request small, follow the project’s conventions even where you’d choose differently, make sure its tests pass, and answer review comments patiently. Maintainers are often volunteers. A focused, well-described pull request that’s easy to say yes to is the best introduction you can make.</p>\n<p>That completes the collaboration toolkit. In the last section you’ll pick up the habits that make all of this safe: undoing mistakes, keeping the repository clean, writing history people can read, and letting automation guard <code dir=\"ltr\">main</code>.</p>\n","toc":[{"id":"sec-1","text":"Issues: work you can link to","level":2},{"id":"sec-2","text":"Projects: the board above the issues","level":2},{"id":"sec-3","text":"Forks: contributing without write access","level":2},{"id":"sec-4","text":"Keeping your fork current","level":2},{"id":"sec-5","text":"Being a welcome contributor","level":2}],"takeaways":["An issue is a unit of planned work or a reported problem; labels, assignees and a linked pull request show its state.","A GitHub Project is a table, board or roadmap view over issues and pull requests, with custom fields like Status and Priority.","A fork is your own copy of someone else’s repository on GitHub; you push to your fork and open a pull request to the original.","By convention <code dir=\"ltr\">origin</code> is your fork and <code dir=\"ltr\">upstream</code> is the original; you fetch from <code dir=\"ltr\">upstream</code> and push to <code dir=\"ltr\">origin</code>.","Before contributing, read CONTRIBUTING.md, look for an existing issue, and keep the change small and on its own branch."],"further":[{"title":"About issues","url":"https://docs.github.com/en/issues/tracking-your-work-with-issues/learning-about-issues/about-issues"},{"title":"About Projects","url":"https://docs.github.com/en/issues/planning-and-tracking-with-projects/learning-about-projects/about-projects"},{"title":"Fork a repository","url":"https://docs.github.com/en/pull-requests/how-tos/work-with-forks/fork-a-repo"},{"title":"Syncing a fork","url":"https://docs.github.com/en/pull-requests/how-tos/work-with-forks/syncing-a-fork"}],"quiz":[{"q":"<p>You cloned your fork of <code dir=\"ltr\">vite-themes</code>. Which commands bring the original project’s newest <code dir=\"ltr\">main</code> into your fork’s <code dir=\"ltr\">main</code>?</p>","options":[{"text":"<code dir=\"ltr\">git pull origin main</code>","why":"<code dir=\"ltr\">origin</code> is your fork, which doesn’t have the new commits yet. You’d be pulling from the copy that’s out of date."},{"text":"<code dir=\"ltr\">git push upstream main</code>","why":"You can’t push to the original repository, and pushing sends your commits rather than fetching theirs."},{"text":"<code dir=\"ltr\">git fetch upstream</code>, <code dir=\"ltr\">git switch main</code>, <code dir=\"ltr\">git merge --ff-only upstream/main</code>, then <code dir=\"ltr\">git push origin main</code>.","why":"Correct. Fetch from the original, fast-forward your <code dir=\"ltr\">main</code> to it, then update your fork on GitHub. The Sync fork button does the GitHub half."},{"text":"<code dir=\"ltr\">git clone</code> the original again into a new folder.","why":"That works but throws away your setup and branches. Adding an <code dir=\"ltr\">upstream</code> remote keeps everything in one clone."}],"answer":2},{"q":"<p>You’ve found a bug in an open-source library and have a fix in mind that touches six files. What should you do first?</p>","options":[{"text":"Open a pull request straight away so the maintainers see working code.","why":"An unannounced large change may clash with plans you don’t know about, and can be closed unread."},{"text":"Email the maintainers privately with the patch attached.","why":"Most projects coordinate in public issues so others can see and help. Private email is for security reports only, if the project asks for it."},{"text":"Fork the project and publish your fixed version under a new name.","why":"That splits the community and leaves everyone else with the bug. Contributing the fix upstream helps everyone."},{"text":"Read CONTRIBUTING.md and search the issues; if none covers it, open one describing the bug and your proposed fix.","why":"Correct. Agreeing on the approach first avoids wasted work, and many projects require an issue before a pull request."}],"answer":3},{"q":"<p>What’s the difference between a label and a GitHub Project?</p>","options":[{"text":"Labels only work on pull requests; Projects only work on issues.","why":"Both labels and Projects work with issues and pull requests."},{"text":"A label tags an individual issue or pull request; a Project is a view across many of them with its own fields, boards and layouts.","why":"Correct. Labels like <code dir=\"ltr\">bug</code> categorise items; a Project arranges items (even from several repositories) into a table, board or roadmap."},{"text":"They’re the same feature with different names.","why":"Labels are simple tags on one repository’s items. Projects add custom fields, views and automation across items."}],"answer":1},{"q":"<p>You opened a pull request from your fork’s <code dir=\"ltr\">main</code> branch. While waiting for review, you start an unrelated fix and commit it to <code dir=\"ltr\">main</code> too. What happens?</p>","options":[{"text":"The unrelated fix appears in your open pull request, because the pull request tracks your <code dir=\"ltr\">main</code>.","why":"Correct. A pull request shows every commit on its branch. Give each contribution its own branch so changes stay separate."},{"text":"Nothing; the pull request froze its commits when you opened it.","why":"Pull requests follow their branch, which is why pushing to it updates them."},{"text":"GitHub rejects the push because a pull request is open.","why":"Open pull requests don’t lock their branches. The push succeeds and the pull request grows."},{"text":"The new commit goes into the original repository directly.","why":"You don’t have write access to the original. Nothing reaches it except through a merged pull request."}],"answer":0}],"exercise":{"kind":"choice","multiple":false,"options":["a","b","c","d"],"correct":["d"],"title":"Contribute a fix you can't push","prompt":"<p>You cloned <code dir=\"ltr\">open-themes/vite-themes</code> directly, fixed a typo on a branch called <code dir=\"ltr\">fix-readme-typo</code>, and ran <code dir=\"ltr\">git push -u origin fix-readme-typo</code>. GitHub answers:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"text\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">Text</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code>ERROR: Permission to open-themes/vite-themes.git denied to maya-okafor.\nfatal: Could not read from remote repository.</code></pre></div>\n<p>What’s the right way to get your fix to the maintainers without redoing the work?</p>\n","hints":["The error isn’t about your SSH key; GitHub knows exactly who you are.","You need a copy of the repository on GitHub that you are allowed to push to.","Your existing clone can talk to more than one remote."],"explanation":"<p>You don’t have write access to <code dir=\"ltr\">open-themes/vite-themes</code>, so no credential will make that push work. Fork it on GitHub, which creates <code dir=\"ltr\">maya-okafor/vite-themes</code>. In your existing clone, rename the current <code dir=\"ltr\">origin</code> to <code dir=\"ltr\">upstream</code> (<code dir=\"ltr\">git remote rename origin upstream</code>) and add your fork as <code dir=\"ltr\">origin</code> (<code dir=\"ltr\">git remote add origin git@github.com:maya-okafor/vite-themes.git</code>). Then <code dir=\"ltr\">git push -u origin fix-readme-typo</code> and open a pull request from your fork’s branch to the original’s <code dir=\"ltr\">main</code>. Your commit is reused as is.</p>\n","checkLabels":{},"optionText":{"a":{"text":"Generate a new SSH key and add it to your account, then push again.","why":"The message names your username, so authentication worked. The problem is permission: you aren’t a collaborator on that repository."},"b":{"text":"Run <code dir=\"ltr\">git push --force -u origin fix-readme-typo</code>.","why":"Forcing changes how a branch is overwritten, not whether you’re allowed to write. The push is still denied."},"c":{"text":"Email the maintainers and ask them to add you as a collaborator so you can push your branch.","why":"Maintainers don’t grant write access for a typo fix. Forks exist so anyone can contribute without it."},"d":{"text":"Fork it on GitHub, make the original <code dir=\"ltr\">upstream</code> and your fork <code dir=\"ltr\">origin</code> in this clone, push the branch to your fork, and open a pull request to the original.","why":"Correct. Your fork is a repository you can push to, and a pull request from it is how changes reach a project you don’t own."}}},"words":778,"readMinutes":4}