{"id":"l-git-1-4","courseId":"git-github","lang":"en","kind":"lesson","summary":"Read a repository’s history with git log, git show and git diff, name any commit relative to HEAD, and picture history as a graph of snapshots linked to their parents.","html":"<p>Your portfolio has a handful of commits now. Six months from now it will have hundreds, and the question you’ll actually ask is never “what’s the history?” but something sharper: when did the footer break, what did this file look like before the redesign, which commit added that colour. Reading history well is how you answer those in seconds.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">The everyday view</h2>\n<p>Plain <code dir=\"ltr\">git log</code> prints every commit, newest first, with full IDs and dates. It’s thorough and tiring. This is the version to remember:</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:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> log</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --oneline</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --graph</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --all</span></span></code></pre></div>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"text\" data-index=\"1\"><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>*   e5d81c0 (HEAD -&gt; main) Merge branch 'dark-mode'\n|\\\n| * d4a7f20 (dark-mode) Add dark mode colors\n* | c39be11 Add projects page\n|/\n* b2e04f3 Add navigation bar with base styles\n* a1c9d72 Add home page</code></pre></div>\n<p>Read it bottom to top for chronology. Each <code dir=\"ltr\">*</code> is a commit; the lines show which commit came from which. In parentheses are labels: <code dir=\"ltr\">main</code> and <code dir=\"ltr\">dark-mode</code> are branches, and <code dir=\"ltr\">HEAD -&gt; main</code> means you’re on <code dir=\"ltr\">main</code>. <code dir=\"ltr\">--all</code> matters: without it, Git shows only commits reachable from where you are, and the <code dir=\"ltr\">dark-mode</code> line would still be there only because it was merged.</p>\n<p>Long output opens in a pager. Use the arrow keys or space to scroll and <code dir=\"ltr\">q</code> to quit.</p>\n<p>Useful filters, all combinable: <code dir=\"ltr\">-n 5</code> (last five), <code dir=\"ltr\">--stat</code> (which files changed), <code dir=\"ltr\">-p</code> (full diffs), <code dir=\"ltr\">--author=&quot;Maya&quot;</code>, <code dir=\"ltr\">--since=&quot;2 weeks ago&quot;</code>, and <code dir=\"ltr\">-- index.html</code> (only commits that touched that file).</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">Commits form a graph</h2>\n<p>The output above is a drawing of a <strong>directed acyclic graph</strong>, or DAG. Directed, because each commit points to its parent. Acyclic, because you can’t follow parents and end up where you started; a commit can’t be its own ancestor. Most commits have one parent. The root commit has none. A <strong>merge commit</strong> has two.</p>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 700 250\" role=\"img\" aria-labelledby=\"l-git-1-4-t1\"><defs><marker id=\"arrow-l-git-1-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-1-4-t1\">Commits a1c9 and b2e0 in a row, then the line splits: c39b on the main line and d4a7 on the dark-mode line. Both lead to merge commit e5d8, which has two parents. The main label and HEAD point to e5d8; the dark-mode label points to d4a7. Arrows point from each commit to its parent.</title>\n  <circle class=\"d-box\" cx=\"60\" cy=\"110\" r=\"26\"/>\n  <text class=\"d-code\" x=\"60\" y=\"115\" text-anchor=\"middle\">a1c9</text>\n  <circle class=\"d-box\" cx=\"190\" cy=\"110\" r=\"26\"/>\n  <text class=\"d-code\" x=\"190\" y=\"115\" text-anchor=\"middle\">b2e0</text>\n  <circle class=\"d-box\" cx=\"330\" cy=\"60\" r=\"26\"/>\n  <text class=\"d-code\" x=\"330\" y=\"65\" text-anchor=\"middle\">c39b</text>\n  <circle class=\"d-box\" cx=\"330\" cy=\"170\" r=\"26\"/>\n  <text class=\"d-code\" x=\"330\" y=\"175\" text-anchor=\"middle\">d4a7</text>\n  <circle class=\"d-box-primary\" cx=\"480\" cy=\"110\" r=\"26\"/>\n  <text class=\"d-code\" x=\"480\" y=\"115\" text-anchor=\"middle\">e5d8</text>\n  <path class=\"d-arrow\" d=\"M164 110 L90 110\" marker-end=\"url(#arrow-l-git-1-4-1)\"/>\n  <path class=\"d-arrow\" d=\"M305 70 L218 102\" marker-end=\"url(#arrow-l-git-1-4-1)\"/>\n  <path class=\"d-arrow\" d=\"M305 160 L218 120\" marker-end=\"url(#arrow-l-git-1-4-1)\"/>\n  <path class=\"d-arrow\" d=\"M455 100 L360 66\" marker-end=\"url(#arrow-l-git-1-4-1)\"/>\n  <path class=\"d-arrow\" d=\"M455 120 L360 164\" marker-end=\"url(#arrow-l-git-1-4-1)\"/>\n  <rect class=\"d-box-accent\" x=\"560\" y=\"70\" width=\"110\" height=\"34\" rx=\"8\"/>\n  <text class=\"d-code\" x=\"615\" y=\"92\" text-anchor=\"middle\">main</text>\n  <rect class=\"d-box-warn\" x=\"560\" y=\"20\" width=\"110\" height=\"34\" rx=\"8\"/>\n  <text class=\"d-code\" x=\"615\" y=\"42\" text-anchor=\"middle\">HEAD</text>\n  <path class=\"d-line\" d=\"M615 54 L615 70\"/>\n  <path class=\"d-line\" d=\"M560 92 L508 106\"/>\n  <rect class=\"d-box-accent\" x=\"400\" y=\"200\" width=\"120\" height=\"34\" rx=\"8\"/>\n  <text class=\"d-code\" x=\"460\" y=\"222\" text-anchor=\"middle\">dark-mode</text>\n  <path class=\"d-line\" d=\"M400 205 L356 178\"/>\n</svg><figcaption>The same history as a commit graph</figcaption></figure>\n<p>Keep this picture in your head. In section 2 you’ll see that a branch is nothing more than one of those labels, and every branching command moves a label or adds a circle.</p>\n<h2 id=\"sec-3\" tabindex=\"-1\">Naming commits</h2>\n<p>You can name any commit by its ID, and Git accepts the shortest unambiguous prefix, usually 7 characters. IDs are hashes of the commit’s content, metadata and parent, so they never change, and in practice two different commits never share one.</p>\n<p>Counting from where you are is often easier:</p>\n<ul>\n<li><code dir=\"ltr\">HEAD</code> is the commit you’re on.</li>\n<li><code dir=\"ltr\">HEAD~1</code> (or <code dir=\"ltr\">HEAD~</code>) is its parent, <code dir=\"ltr\">HEAD~3</code> is three generations back, following the first parent each time.</li>\n<li><code dir=\"ltr\">HEAD^2</code> is the <em>second</em> parent of a merge commit; on <code dir=\"ltr\">e5d81c0</code> above, that’s <code dir=\"ltr\">d4a7f20</code>.</li>\n<li>A branch name works anywhere a commit does: <code dir=\"ltr\">dark-mode~1</code> is <code dir=\"ltr\">b2e04f3</code>.</li>\n</ul>\n<h2 id=\"sec-4\" tabindex=\"-1\">Looking inside commits</h2>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"2\"><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\"> show</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> c39be11</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">              # message, author, date and the diff it introduced</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> show</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> HEAD~2:styles.css</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">    # the whole file as it was in that snapshot</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> diff</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> a1c9d72</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> HEAD</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">         # everything that changed between two snapshots</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> diff</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> HEAD~3</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> HEAD</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> styles.css</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">   # the same, for one file only</span></span></code></pre></div>\n<p><code dir=\"ltr\">git diff</code> takes any two commits because each is a complete snapshot. The diff is calculated on demand by comparing them. The same goes for the diff <code dir=\"ltr\">git show</code> prints: it’s this commit’s snapshot compared with its parent’s.</p>\n<h2 id=\"sec-5\" tabindex=\"-1\">A real investigation</h2>\n<p>Here’s how these commands combine. A friend tells you the footer on your projects page has disappeared, and you’re sure it was there last week. Start by narrowing the history to the file that matters:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"3\"><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\"> log</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --oneline</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --since=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"1 week ago\"</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> projects.html</span></span></code></pre></div>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"text\" data-index=\"4\"><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>8f1e2aa Move project cards into a grid\nc39be11 Add projects page</code></pre></div>\n<p>Two suspects. Look at the newer one in full:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"5\"><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\"> show</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 8f1e2aa</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> projects.html</span></span></code></pre></div>\n<p>The diff shows the <code dir=\"ltr\">&lt;footer&gt;</code> block in red, removed along with the old list markup. That’s your culprit, found in under a minute. To grab the old footer markup without rewinding anything, print the file as it was in the parent commit and copy what you need:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"6\"><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\"> show</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 8f1e2aa~1:projects.html</span></span></code></pre></div>\n<p>Nothing in this investigation changed a single file or commit. Reading history is always safe, so do it freely and often. Most “Git emergencies” I’ve seen were really reading problems: someone panicked before looking at the graph, and the answer was already sitting in <code dir=\"ltr\">git log</code>.</p>\n<aside class=\"callout callout-tip\" 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=\"M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5\"/><path d=\"M9 18h6\"/><path d=\"M10 22h4\"/></svg><span><span class=\"callout-kind\">Tip</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>Find the commit that added something</span></p>\n<p><code dir=\"ltr\">git log -S &quot;dark-mode&quot; --oneline</code> lists commits that added or removed the string <code dir=\"ltr\">dark-mode</code> anywhere in the code. When you need to know when and why a line appeared, this pickaxe search beats scrolling through <code dir=\"ltr\">git log -p</code>.</p>\n</aside>\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>Thinking git log shows everything</span></p>\n<p><code dir=\"ltr\">git log</code> starts at HEAD and follows parents backwards. Commits on other branches that haven’t been merged aren’t reachable from there, so they don’t appear, and people conclude their work is gone. Add <code dir=\"ltr\">--all</code>, or name the branch: <code dir=\"ltr\">git log --oneline dark-mode</code>.</p>\n</aside>\n<p>You’ve seen labels like <code dir=\"ltr\">main</code> and <code dir=\"ltr\">dark-mode</code> sitting on commits. Section 2 starts with what those labels really are and why creating one costs almost nothing.</p>\n","toc":[{"id":"sec-1","text":"The everyday view","level":2},{"id":"sec-2","text":"Commits form a graph","level":2},{"id":"sec-3","text":"Naming commits","level":2},{"id":"sec-4","text":"Looking inside commits","level":2},{"id":"sec-5","text":"A real investigation","level":2}],"takeaways":["History is a directed acyclic graph, each commit pointing to its parent, and a merge commit pointing to two.","<code dir=\"ltr\">git log --oneline --graph --all</code> draws the graph in the terminal, including branches you’re not on.","<code dir=\"ltr\">git show &lt;commit&gt;</code> prints one commit’s message and changes; <code dir=\"ltr\">git show &lt;commit&gt;:&lt;path&gt;</code> prints a file as it was then.","<code dir=\"ltr\">HEAD~1</code> is the commit before the one you’re on, <code dir=\"ltr\">HEAD~3</code> three steps back along first parents.","<code dir=\"ltr\">git diff A B</code> compares any two snapshots, and <code dir=\"ltr\">-- &lt;path&gt;</code> limits it to one file."],"further":[{"title":"Viewing the Commit History (Pro Git)","url":"https://git-scm.com/book/en/v2/Git-Basics-Viewing-the-Commit-History"},{"title":"git log reference","url":"https://git-scm.com/docs/git-log"},{"title":"gitrevisions (naming commits)","url":"https://git-scm.com/docs/gitrevisions"}],"quiz":[{"q":"<p><code dir=\"ltr\">git log</code> on <code dir=\"ltr\">main</code> doesn’t show a commit you made on your <code dir=\"ltr\">dark-mode</code> branch. Why?</p>","options":[{"text":"The commit was lost because you switched branches.","why":"Switching branches never deletes commits. The commit is still there, pointed at by <code dir=\"ltr\">dark-mode</code>."},{"text":"<code dir=\"ltr\">git log</code> only shows commits reachable from HEAD by following parents, and that commit isn’t.","why":"Correct. <code dir=\"ltr\">main</code> doesn’t lead back to it. <code dir=\"ltr\">git log --all</code> or <code dir=\"ltr\">git log dark-mode</code> will show it."},{"text":"<code dir=\"ltr\">git log</code> hides commits until they’re pushed.","why":"Git doesn’t care about pushing when it shows local history. Unpushed commits appear like any others."},{"text":"You need <code dir=\"ltr\">git log -p</code> to see commits from other branches.","why":"<code dir=\"ltr\">-p</code> adds each commit’s diff to the output. It doesn’t change which commits are listed."}],"answer":1},{"q":"<p>You want to see <code dir=\"ltr\">styles.css</code> exactly as it was two commits ago, without changing any files. Which command does that?</p>","options":[{"text":"<code dir=\"ltr\">git restore styles.css~2</code>","why":"<code dir=\"ltr\">~2</code> applies to commits, not file names, and <code dir=\"ltr\">git restore</code> writes into your working tree, which you didn’t want."},{"text":"<code dir=\"ltr\">git log -2 styles.css</code>","why":"This lists the last two commits that touched the file. It shows messages, not the file’s contents."},{"text":"<code dir=\"ltr\">git diff HEAD~2</code>","why":"This shows changes between that commit and your working tree, not the file’s full content."},{"text":"<code dir=\"ltr\">git show HEAD~2:styles.css</code>","why":"Correct. <code dir=\"ltr\">&lt;commit&gt;:&lt;path&gt;</code> names a file inside a snapshot, and <code dir=\"ltr\">git show</code> prints it without touching your files."}],"answer":3},{"q":"<p>How many parents does a merge commit that joins <code dir=\"ltr\">dark-mode</code> into <code dir=\"ltr\">main</code> have?</p>","options":[{"text":"Two, the commit <code dir=\"ltr\">main</code> was on and the commit <code dir=\"ltr\">dark-mode</code> was on.","why":"Correct. Two parents is what makes it a merge, and it’s why the graph joins at that point."},{"text":"One, the commit <code dir=\"ltr\">main</code> was on before the merge.","why":"A single parent is an ordinary commit. A merge records both histories it joins."},{"text":"None, because a merge commit starts a new history.","why":"Only the very first commit in a repository (the root commit) has no parent."}],"answer":0},{"q":"<p>Which command lists only the commits whose changes added or removed the text <code dir=\"ltr\">dark-mode</code>?</p>","options":[{"text":"<code dir=\"ltr\">git log --grep dark-mode</code>","why":"<code dir=\"ltr\">--grep</code> searches commit messages, not the code changes inside commits."},{"text":"<code dir=\"ltr\">git diff dark-mode</code>","why":"That compares your working tree against the <code dir=\"ltr\">dark-mode</code> branch. It doesn’t list commits."},{"text":"<code dir=\"ltr\">git log -S dark-mode</code>","why":"Correct. The pickaxe option <code dir=\"ltr\">-S</code> finds commits that changed how many times the string appears, which is how you hunt down when something was added or removed."},{"text":"<code dir=\"ltr\">git show dark-mode</code>","why":"That shows the commit the <code dir=\"ltr\">dark-mode</code> branch points to, if such a branch exists."}],"answer":2}],"exercise":{"kind":"choice","multiple":false,"options":["a","b","c","d"],"correct":["c"],"title":"Read the graph","prompt":"<p>Here is the portfolio’s history as a commit graph, the same shape <code dir=\"ltr\">git log --oneline --graph --all</code> draws in text. Arrows point from each commit to its parent.</p>\n<p>:::figure The portfolio’s commit graph\n<svg viewBox=\"0 0 640 230\" role=\"img\" aria-labelledby=\"ex1\"></p>\n  <title id=\"ex1\">Five commits. 1a2b and 3c4d in a row. From 3c4d, one line goes to 5e6f and another to 7a8b. Merge commit 9c0d has parents 5e6f and 7a8b. main and HEAD point to 9c0d; the label hero points to 7a8b.</title>\n  <circle class=\"d-box\" cx=\"50\" cy=\"110\" r=\"26\"/>\n  <text class=\"d-code\" x=\"50\" y=\"115\" text-anchor=\"middle\">1a2b</text>\n  <circle class=\"d-box\" cx=\"170\" cy=\"110\" r=\"26\"/>\n  <text class=\"d-code\" x=\"170\" y=\"115\" text-anchor=\"middle\">3c4d</text>\n  <circle class=\"d-box\" cx=\"300\" cy=\"55\" r=\"26\"/>\n  <text class=\"d-code\" x=\"300\" y=\"60\" text-anchor=\"middle\">5e6f</text>\n  <circle class=\"d-box\" cx=\"300\" cy=\"165\" r=\"26\"/>\n  <text class=\"d-code\" x=\"300\" y=\"170\" text-anchor=\"middle\">7a8b</text>\n  <circle class=\"d-box-primary\" cx=\"440\" cy=\"110\" r=\"26\"/>\n  <text class=\"d-code\" x=\"440\" y=\"115\" text-anchor=\"middle\">9c0d</text>\n  <path class=\"d-arrow\" d=\"M144 110 L80 110\" marker-end=\"url(#arrow)\"/>\n  <path class=\"d-arrow\" d=\"M275 64 L198 100\" marker-end=\"url(#arrow)\"/>\n  <path class=\"d-arrow\" d=\"M275 156 L198 120\" marker-end=\"url(#arrow)\"/>\n  <path class=\"d-arrow\" d=\"M415 100 L330 62\" marker-end=\"url(#arrow)\"/>\n  <path class=\"d-arrow\" d=\"M415 120 L330 160\" marker-end=\"url(#arrow)\"/>\n  <rect class=\"d-box-accent\" x=\"510\" y=\"93\" width=\"100\" height=\"34\" rx=\"8\"/>\n  <text class=\"d-code\" x=\"560\" y=\"115\" text-anchor=\"middle\">main</text>\n  <rect class=\"d-box-warn\" x=\"510\" y=\"40\" width=\"100\" height=\"34\" rx=\"8\"/>\n  <text class=\"d-code\" x=\"560\" y=\"62\" text-anchor=\"middle\">HEAD</text>\n  <path class=\"d-line\" d=\"M560 74 L560 93\"/>\n  <rect class=\"d-box-accent\" x=\"370\" y=\"185\" width=\"100\" height=\"34\" rx=\"8\"/>\n  <text class=\"d-code\" x=\"420\" y=\"207\" text-anchor=\"middle\">hero</text>\n  <path class=\"d-line\" d=\"M370 195 L328 172\"/>\n</svg>\n:::\n<p>You’re on <code dir=\"ltr\">main</code>. Which commits does <code dir=\"ltr\">git log --oneline hero</code> list?</p>\n","hints":["<code dir=\"ltr\">git log &lt;name&gt;</code> starts at the commit that name points to.","From there it follows parent arrows only. It never walks forward to children.","Start at the commit labelled <code dir=\"ltr\">hero</code> and follow the arrows until you run out."],"explanation":"<p><code dir=\"ltr\">git log hero</code> starts at <code dir=\"ltr\">7a8b</code>, the commit the <code dir=\"ltr\">hero</code> branch points to, and follows parents: <code dir=\"ltr\">7a8b</code> → <code dir=\"ltr\">3c4d</code> → <code dir=\"ltr\">1a2b</code>. The merge commit <code dir=\"ltr\">9c0d</code> is a <em>child</em> of <code dir=\"ltr\">7a8b</code>, and Git never walks from parent to child, so it isn’t listed. <code dir=\"ltr\">5e6f</code> is on the other side of the fork and isn’t an ancestor of <code dir=\"ltr\">hero</code> either.</p>\n","checkLabels":{},"optionText":{"a":{"text":"All five commits, because <code dir=\"ltr\">hero</code> was merged into <code dir=\"ltr\">main</code>.","why":"Merging made <code dir=\"ltr\">hero</code>’s commits reachable from <code dir=\"ltr\">main</code>, not the other way round. <code dir=\"ltr\">git log hero</code> starts at <code dir=\"ltr\">hero</code> and only walks backwards."},"b":{"text":"Only <code dir=\"ltr\">7a8b</code>, the commit <code dir=\"ltr\">hero</code> points to.","why":"<code dir=\"ltr\">git log</code> lists the starting commit and every ancestor, not one commit. <code dir=\"ltr\">git show hero</code> would print only <code dir=\"ltr\">7a8b</code>."},"c":{"text":"<code dir=\"ltr\">7a8b</code>, <code dir=\"ltr\">3c4d</code> and <code dir=\"ltr\">1a2b</code>.","why":"Correct. Start at <code dir=\"ltr\">7a8b</code> and follow parents: <code dir=\"ltr\">3c4d</code>, then the root commit <code dir=\"ltr\">1a2b</code>."},"d":{"text":"<code dir=\"ltr\">9c0d</code>, <code dir=\"ltr\">7a8b</code>, <code dir=\"ltr\">3c4d</code> and <code dir=\"ltr\">1a2b</code>.","why":"<code dir=\"ltr\">9c0d</code> is a child of <code dir=\"ltr\">7a8b</code>. Commits don’t know their children, so <code dir=\"ltr\">git log</code> can’t reach it from <code dir=\"ltr\">hero</code>."}}},"words":706,"readMinutes":3}