-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathgit.html
More file actions
1987 lines (1556 loc) · 83.3 KB
/
git.html
File metadata and controls
1987 lines (1556 loc) · 83.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Master Git - Version Control from Zero to Advanced - Better Dev</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css">
</head>
<body>
<header class="topbar">
<button class="sidebar-toggle" aria-label="Open navigation" aria-expanded="false">
<span class="hamburger-icon"></span>
</button>
<a href="index.html" class="logo">Better Dev</a>
</header>
<div class="sidebar-backdrop" aria-hidden="true"></div>
<aside class="sidebar" aria-label="Site navigation">
<div class="sidebar-header">
<span class="sidebar-title">Navigation</span>
<button class="sidebar-close" aria-label="Close navigation">×</button>
</div>
<div class="sidebar-search">
<input type="text" class="sidebar-search-input" placeholder="Search topics..." aria-label="Search topics">
<div class="sidebar-search-results"></div>
</div>
<nav class="sidebar-nav">
<div class="sidebar-group">
<a href="index.html">Home</a>
</div>
<div class="sidebar-group">
<div class="sidebar-group-label">Mathematics</div>
<a href="pre-algebra.html">Pre-Algebra</a>
<a href="algebra.html">Algebra</a>
<a href="sequences-series.html">Sequences & Series</a>
<a href="geometry.html">Geometry</a>
<a href="calculus.html">Calculus</a>
<a href="discrete-math.html">Discrete Math</a>
<a href="linear-algebra.html">Linear Algebra</a>
<a href="probability.html">Probability & Statistics</a>
<a href="binary-systems.html">Binary & Number Systems</a>
<a href="number-theory.html">Number Theory for CP</a>
<a href="computational-geometry.html">Computational Geometry</a>
<a href="game-theory.html">Game Theory</a>
</div>
<div class="sidebar-group">
<div class="sidebar-group-label">Data Structures & Algorithms</div>
<a href="dsa-foundations.html">DSA Foundations</a>
<a href="arrays.html">Arrays & Strings</a>
<a href="stacks-queues.html">Stacks & Queues</a>
<a href="hashmaps.html">Hash Maps & Sets</a>
<a href="linked-lists.html">Linked Lists</a>
<a href="trees.html">Trees & BST</a>
<a href="graphs.html">Graphs</a>
<a href="sorting.html">Sorting & Searching</a>
<a href="patterns.html">LeetCode Patterns</a>
<a href="dp.html">Dynamic Programming</a>
<a href="advanced.html">Advanced Topics</a>
<a href="string-algorithms.html">String Algorithms</a>
<a href="advanced-graphs.html">Advanced Graphs</a>
<a href="advanced-dp.html">Advanced DP</a>
<a href="advanced-ds.html">Advanced Data Structures</a>
<a href="leetcode-650.html">The 650 Problems</a>
<a href="competitive-programming.html">CP Roadmap</a>
</div>
<div class="sidebar-group">
<div class="sidebar-group-label">Languages & Systems</div>
<a href="cpp.html">C++</a>
<a href="golang.html">Go</a>
<a href="javascript.html">JavaScript Deep Dive</a>
<a href="typescript.html">TypeScript</a>
<a href="nodejs.html">Node.js Internals</a>
<a href="os.html">Operating Systems</a>
<a href="linux.html">Linux</a>
<a href="git.html">Git</a>
<a href="backend.html">Backend</a>
<a href="system-design.html">System Design</a>
<a href="networking.html">Networking</a>
<a href="cloud.html">Cloud & Infrastructure</a>
<a href="docker.html">Docker & Compose</a>
<a href="kubernetes.html">Kubernetes</a>
<a href="message-queues.html">Queues & Pub/Sub</a>
<a href="selfhosting.html">VPS & Self-Hosting</a>
<a href="databases.html">PostgreSQL & MySQL</a>
<a href="stripe.html">Stripe & Payments</a>
<a href="distributed-systems.html">Distributed Systems</a>
<a href="backend-engineering.html">Backend Engineering</a>
</div>
<div class="sidebar-group">
<div class="sidebar-group-label">JS/TS Ecosystem</div>
<a href="js-tooling.html">Tooling & Bundlers</a>
<a href="js-testing.html">Testing</a>
<a href="ts-projects.html">Building with TS</a>
</div>
<div class="sidebar-group">
<div class="sidebar-group-label">More</div>
<a href="seans-brain.html">Sean's Brain</a>
</div>
</nav>
</aside>
<div class="container">
<div class="page-header">
<div class="breadcrumb"><a href="index.html">Home</a> › Master Git</div>
<h1>Master Git</h1>
<p>Stop memorizing commands. Understand how Git actually works -- snapshots, pointers, the DAG, the reflog -- and every command becomes obvious. This guide takes you from zero to confidently rebasing, cherry-picking, and rewriting history.</p>
<div class="tip-box" style="margin-top: 1rem;">
<div class="label">Mental Model First</div>
<p>Most people struggle with Git because they learn commands without understanding the data model. This page teaches you the model first. Once you see that branches are just pointers and commits are just snapshots, everything clicks.</p>
</div>
</div>
</div>
<div class="page-with-toc">
<aside class="sidebar-toc">
<div class="toc">
<h4>Table of Contents</h4>
<a href="#what-is-git">1. What Is Git?</a>
<a href="#install-setup">2. Install & Setup</a>
<a href="#internals">3. How Git Works Under the Hood</a>
<a href="#three-areas">4. The Three Areas</a>
<a href="#basic-workflow">5. Basic Workflow</a>
<a href="#branches-pointers">6. Branches & Pointers</a>
<a href="#merging">7. Merging</a>
<a href="#history">8. Reading & Navigating History</a>
<a href="#undoing">9. Undoing Things (Reset, Revert, Restore)</a>
<a href="#rebase">10. Rebase</a>
<a href="#cherry-pick-squash">11. Cherry-Pick & Squash</a>
<a href="#stashing">12. Stashing</a>
<a href="#remotes">13. Remotes & Collaboration</a>
<a href="#gh-cli">14. GitHub CLI (gh) & GitLab CLI (glab)</a>
<a href="#advanced">15. Advanced Git</a>
<a href="#quiz">16. Practice Quiz</a>
</div>
</aside>
<div class="content">
<!-- Mobile TOC -->
<div class="toc" style="margin-bottom: 2rem;">
<h4>Table of Contents</h4>
<a href="#what-is-git">1. What Is Git?</a>
<a href="#install-setup">2. Install & Setup</a>
<a href="#internals">3. How Git Works Under the Hood</a>
<a href="#three-areas">4. The Three Areas</a>
<a href="#basic-workflow">5. Basic Workflow</a>
<a href="#branches-pointers">6. Branches & Pointers</a>
<a href="#merging">7. Merging</a>
<a href="#history">8. Reading & Navigating History</a>
<a href="#undoing">9. Undoing Things (Reset, Revert, Restore)</a>
<a href="#rebase">10. Rebase</a>
<a href="#cherry-pick-squash">11. Cherry-Pick & Squash</a>
<a href="#stashing">12. Stashing</a>
<a href="#remotes">13. Remotes & Collaboration</a>
<a href="#gh-cli">14. GitHub CLI (gh) & GitLab CLI (glab)</a>
<a href="#advanced">15. Advanced Git</a>
<a href="#quiz">16. Practice Quiz</a>
</div>
<!-- ============================================================ -->
<!-- SECTION 1: WHAT IS GIT? -->
<!-- ============================================================ -->
<section id="what-is-git">
<h2>1. What Is Git?</h2>
<p>Git is a <strong>distributed version control system</strong>. It tracks every change you make to your code, lets you go back to any point in time, and lets multiple people work on the same project without destroying each other's work.</p>
<p>Created by Linus Torvalds in 2005 (yes, the same person who made Linux) because the existing tools were too slow for the Linux kernel's massive codebase.</p>
<h3>Why Git Matters</h3>
<ul>
<li><strong>Time travel</strong> -- go back to any previous version of your code</li>
<li><strong>Parallel work</strong> -- branches let you work on features without breaking main</li>
<li><strong>Collaboration</strong> -- multiple developers can work on the same repo</li>
<li><strong>Safety net</strong> -- almost nothing in Git is truly lost (reflog saves you)</li>
<li><strong>Industry standard</strong> -- every company, every open-source project uses Git</li>
</ul>
<div class="warning-box">
<div class="label">Git is NOT GitHub</div>
<p><strong>Git</strong> is the version control tool that runs on your machine. <strong>GitHub</strong> (and GitLab, Bitbucket) are hosting platforms that store Git repositories online. You can use Git without GitHub. GitHub just makes collaboration easier.</p>
</div>
<h3>The Key Insight: Snapshots, Not Diffs</h3>
<p>Most people think Git stores "changes" (diffs). It doesn't. Git stores <strong>snapshots</strong> of your entire project at each commit. Every commit is a complete picture of every file at that moment.</p>
<div class="formula-box">
<pre>
Other VCS (SVN, etc.): Store CHANGES between versions
v1 → [+3 lines] → v2 → [-1 line, +5 lines] → v3
Git: Stores SNAPSHOTS of the entire project
Commit A = snapshot of all files at time A
Commit B = snapshot of all files at time B
Commit C = snapshot of all files at time C
(If a file didn't change, Git just stores a pointer to the
previous version -- so it's still space-efficient)
</pre>
</div>
<p>This is why Git is so fast. To show you "version 3" of your project, it doesn't have to replay changes 1→2→3. It just loads snapshot 3 directly.</p>
</section>
<!-- ============================================================ -->
<!-- SECTION 2: INSTALL & SETUP -->
<!-- ============================================================ -->
<section id="install-setup">
<h2>2. Install & Setup</h2>
<h3>Installing Git</h3>
<div class="code-pair">
<pre><code><span class="lang-label">Linux (Debian/Ubuntu/Pop!_OS)</span>
<span class="comment"># Git comes pre-installed on most distros, but just in case:</span>
sudo apt update
sudo apt install git
<span class="comment"># Verify installation</span>
git --version
<span class="comment"># git version 2.43.0</span></code></pre>
<pre><code><span class="lang-label">macOS</span>
<span class="comment"># Git comes with Xcode Command Line Tools:</span>
xcode-select --install
<span class="comment"># Or install via Homebrew:</span>
brew install git
<span class="comment"># Verify</span>
git --version</code></pre>
</div>
<h3>First-Time Configuration</h3>
<p>Git needs to know who you are. This info gets attached to every commit you make.</p>
<pre><code><span class="lang-label">Bash</span>
<span class="comment"># Set your identity (REQUIRED -- do this first)</span>
git config --global user.name <span class="string">"Your Name"</span>
git config --global user.email <span class="string">"your.email@example.com"</span>
<span class="comment"># Set default branch name to "main" (instead of "master")</span>
git config --global init.defaultBranch main
<span class="comment"># Set your default editor (for commit messages, rebase, etc.)</span>
git config --global core.editor <span class="string">"vim"</span> <span class="comment"># or nano, or code --wait</span>
<span class="comment"># Enable colored output</span>
git config --global color.ui auto
<span class="comment"># View all your settings</span>
git config --list</code></pre>
<div class="tip-box">
<div class="label">Config Levels</div>
<p><code>--global</code> applies to all repos for your user (~/.gitconfig). <code>--local</code> (default) applies only to the current repo (.git/config). <code>--system</code> applies to every user on the machine (/etc/gitconfig). Local overrides global, global overrides system.</p>
</div>
<h3>SSH Key Setup (Stop Typing Your Password)</h3>
<p>SSH keys let you push/pull without entering your password every time. You create a key pair: a private key (stays on your machine, NEVER share) and a public key (give to GitHub/GitLab).</p>
<pre><code><span class="lang-label">Bash</span>
<span class="comment"># 1. Generate an SSH key pair</span>
ssh-keygen -t ed25519 -C <span class="string">"your.email@example.com"</span>
<span class="comment"># Press Enter for default location (~/.ssh/id_ed25519)</span>
<span class="comment"># Enter a passphrase (recommended) or leave empty</span>
<span class="comment"># 2. Start the SSH agent</span>
eval <span class="string">"$(ssh-agent -s)"</span>
<span class="comment"># 3. Add your private key to the agent</span>
ssh-add ~/.ssh/id_ed25519
<span class="comment"># 4. Copy your PUBLIC key</span>
cat ~/.ssh/id_ed25519.pub
<span class="comment"># Copy the output</span>
<span class="comment"># 5. Go to GitHub → Settings → SSH Keys → New SSH Key</span>
<span class="comment"># Paste the public key. Done.</span>
<span class="comment"># 6. Test it</span>
ssh -T git@github.com
<span class="comment"># "Hi username! You've successfully authenticated..."</span></code></pre>
<h3>Installing GitHub CLI (gh)</h3>
<pre><code><span class="lang-label">Bash</span>
<span class="comment"># Linux (Debian/Ubuntu/Pop!_OS)</span>
sudo apt install gh
<span class="comment"># Or via Homebrew (macOS/Linux)</span>
brew install gh
<span class="comment"># Authenticate</span>
gh auth login
<span class="comment"># Follow the prompts -- choose SSH, browser auth</span>
<span class="comment"># Verify</span>
gh auth status</code></pre>
<h3>Installing GitLab CLI (glab)</h3>
<pre><code><span class="lang-label">Bash</span>
<span class="comment"># Via Homebrew (macOS/Linux)</span>
brew install glab
<span class="comment"># Or download from releases</span>
<span class="comment"># https://gitlab.com/gitlab-org/cli/-/releases</span>
<span class="comment"># Authenticate</span>
glab auth login
<span class="comment"># Choose your GitLab instance, create a personal access token</span>
<span class="comment"># Verify</span>
glab auth status</code></pre>
<h3>Useful Aliases</h3>
<pre><code><span class="lang-label">Bash</span>
<span class="comment"># Create short aliases for common commands</span>
git config --global alias.st status
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.lg <span class="string">"log --oneline --graph --all --decorate"</span>
<span class="comment"># Now you can use:</span>
git st <span class="comment"># instead of git status</span>
git lg <span class="comment"># beautiful commit graph</span></code></pre>
</section>
<!-- ============================================================ -->
<!-- SECTION 3: HOW GIT WORKS UNDER THE HOOD -->
<!-- ============================================================ -->
<section id="internals">
<h2>3. How Git Works Under the Hood</h2>
<p>Understanding Git's internals turns you from someone who memorizes commands into someone who <em>knows</em> what's happening. Everything lives in the <code>.git</code> directory.</p>
<h3>The .git Directory</h3>
<pre><code><span class="lang-label">Bash</span>
my-project/
├── .git/ <span class="comment"># ALL of Git lives here</span>
│ ├── objects/ <span class="comment"># All commits, trees, blobs (the actual data)</span>
│ ├── refs/ <span class="comment"># Branch and tag pointers</span>
│ │ ├── heads/ <span class="comment"># Local branches (main, feature-x, etc.)</span>
│ │ ├── remotes/ <span class="comment"># Remote-tracking branches</span>
│ │ └── tags/ <span class="comment"># Tag references</span>
│ ├── HEAD <span class="comment"># Points to current branch (or commit)</span>
│ ├── config <span class="comment"># Repo-specific configuration</span>
│ ├── index <span class="comment"># The staging area (binary file)</span>
│ └── logs/ <span class="comment"># Reflog -- history of HEAD movements</span>
├── src/
├── README.md
└── ...</code></pre>
<div class="warning-box">
<div class="label">Never Manually Edit .git</div>
<p>If you delete the .git folder, you lose ALL history. Your files stay, but every commit, branch, and remote is gone. Treat .git as sacred.</p>
</div>
<h3>Git's Four Object Types</h3>
<p>Everything Git stores is one of four types of objects, identified by a SHA-1 hash (40-character hex string):</p>
<table>
<thead>
<tr><th>Object</th><th>What It Stores</th><th>Analogy</th></tr>
</thead>
<tbody>
<tr><td><strong>Blob</strong></td><td>File contents (just the raw data, not the filename)</td><td>A single file</td></tr>
<tr><td><strong>Tree</strong></td><td>A directory listing -- maps filenames to blobs and other trees</td><td>A folder</td></tr>
<tr><td><strong>Commit</strong></td><td>A snapshot -- points to a tree + metadata (author, message, parent commit)</td><td>A save point</td></tr>
<tr><td><strong>Tag</strong></td><td>A named pointer to a commit (annotated tags have extra metadata)</td><td>A bookmark</td></tr>
</tbody>
</table>
<div class="formula-box">
<pre>
How a commit is structured:
commit abc123...
├── tree def456... (root directory snapshot)
│ ├── blob 111aaa... → README.md
│ ├── blob 222bbb... → index.html
│ └── tree 333ccc... → src/
│ ├── blob 444ddd... → app.js
│ └── blob 555eee... → style.css
├── parent 789xyz... (previous commit)
├── author Sean <sean@...>
└── message "Add login page"
</pre>
</div>
<h3>SHA-1 Hashes: Git's Content Addressing</h3>
<p>Every object in Git is identified by a SHA-1 hash of its contents. This means:</p>
<ul>
<li>If two files have identical content, they share the same blob (deduplication)</li>
<li>If a commit's hash is <code>abc123</code>, you can always refer to it by that hash</li>
<li>You only need the first 7-8 characters (Git auto-resolves: <code>abc123f</code>)</li>
<li>It's impossible to change history without changing hashes -- Git is tamper-proof</li>
</ul>
<pre><code><span class="lang-label">Bash</span>
<span class="comment"># See the raw content of any Git object</span>
git cat-file -p HEAD <span class="comment"># Show current commit's data</span>
git cat-file -p HEAD:README.md <span class="comment"># Show a file at HEAD</span>
git cat-file -t abc123f <span class="comment"># Show type: commit, tree, blob, tag</span></code></pre>
</section>
<!-- ============================================================ -->
<!-- SECTION 4: THE THREE AREAS -->
<!-- ============================================================ -->
<section id="three-areas">
<h2>4. The Three Areas</h2>
<p>This is the single most important concept in Git. Every file lives in one of three areas:</p>
<div class="formula-box">
<pre>
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ WORKING │ │ STAGING AREA │ │ REPOSITORY │
│ DIRECTORY │ │ (Index) │ │ (.git) │
│ │ │ │ │ │
│ Your actual │ │ Files ready │ │ Permanent │
│ files on disk │ ───► │ to be committed │ ───► │ snapshots │
│ │ │ │ │ (commits) │
│ │ git │ │ git │ │
│ │ add │ │commit│ │
└─────────────────┘ └─────────────────┘ └─────────────────┘
</pre>
</div>
<h3>Working Directory</h3>
<p>Your actual files on disk. When you edit a file in your editor, you're changing the working directory. Git sees it as "modified" but won't track the change until you stage it.</p>
<h3>Staging Area (Index)</h3>
<p>A holding area for changes you want in your next commit. <code>git add</code> moves changes here. Think of it as "I want to include this change in my next commit". You can stage some files and leave others unstaged -- this gives you precise control over what goes into each commit.</p>
<h3>Repository (.git)</h3>
<p>The permanent history. When you <code>git commit</code>, everything in the staging area becomes a new snapshot in the repository. Commits are (almost) permanent.</p>
<div class="example-box">
<div class="label">Example: Selective Staging</div>
<pre>
You changed 3 files: auth.js, style.css, debug-notes.txt
You only want to commit the auth and CSS changes:
git add auth.js style.css ← stage only these two
git commit -m "Add login styling"
debug-notes.txt stays as an unstaged change in your
working directory. It won't be in the commit.
</pre>
</div>
<h3>File States</h3>
<table>
<thead>
<tr><th>State</th><th>Meaning</th><th>Where</th></tr>
</thead>
<tbody>
<tr><td><strong>Untracked</strong></td><td>New file Git has never seen</td><td>Working directory</td></tr>
<tr><td><strong>Modified</strong></td><td>Tracked file that has changed</td><td>Working directory</td></tr>
<tr><td><strong>Staged</strong></td><td>Change marked for next commit</td><td>Staging area</td></tr>
<tr><td><strong>Committed</strong></td><td>Safely stored in repository</td><td>.git</td></tr>
</tbody>
</table>
</section>
<!-- ============================================================ -->
<!-- SECTION 5: BASIC WORKFLOW -->
<!-- ============================================================ -->
<section id="basic-workflow">
<h2>5. Basic Workflow</h2>
<h3>Creating a Repository</h3>
<pre><code><span class="lang-label">Bash</span>
<span class="comment"># Option 1: Start a new project</span>
mkdir my-project
cd my-project
git init
<span class="comment"># Creates .git directory. You now have a repo with no commits.</span>
<span class="comment"># Option 2: Clone an existing repo</span>
git clone git@github.com:username/repo.git
<span class="comment"># Downloads entire repo + history. Sets up remote "origin" automatically.</span>
<span class="comment"># Clone into a specific folder name</span>
git clone git@github.com:username/repo.git my-folder</code></pre>
<h3>The Core Loop</h3>
<pre><code><span class="lang-label">Bash</span>
<span class="comment"># 1. Check what's changed</span>
git status
<span class="comment"># 2. See the actual changes (diff)</span>
git diff <span class="comment"># Unstaged changes</span>
git diff --staged <span class="comment"># Staged changes (what will be committed)</span>
<span class="comment"># 3. Stage changes</span>
git add filename.js <span class="comment"># Stage a specific file</span>
git add src/ <span class="comment"># Stage an entire directory</span>
git add -p <span class="comment"># Stage interactively (pick specific hunks)</span>
<span class="comment"># 4. Commit</span>
git commit -m <span class="string">"Add user authentication"</span>
<span class="comment"># 5. Push to remote</span>
git push</code></pre>
<div class="warning-box">
<div class="label">Avoid git add .</div>
<p><code>git add .</code> stages EVERYTHING. This can accidentally include .env files, node_modules, debug logs, or other junk. Prefer staging specific files by name, or use <code>git add -p</code> to review each change before staging.</p>
</div>
<h3>Writing Good Commit Messages</h3>
<pre><code><span class="lang-label">Bash</span>
<span class="comment"># Short message for small changes:</span>
git commit -m <span class="string">"Fix null pointer in user login"</span>
<span class="comment"># Multi-line message (opens your editor):</span>
git commit
<span class="comment"># In the editor:</span>
<span class="comment"># Line 1: Short summary (50 chars max, imperative mood)</span>
<span class="comment"># Line 2: blank</span>
<span class="comment"># Line 3+: Detailed explanation of WHY (not what)</span></code></pre>
<div class="tip-box">
<div class="label">Commit Message Convention</div>
<p>Use imperative mood: "Add feature" not "Added feature". Think of it as completing the sentence: "If applied, this commit will <em>add feature</em>." Keep the first line under 50 characters. Explain <strong>why</strong> in the body, not what (the diff shows what).</p>
</div>
<h3>.gitignore</h3>
<p>Tell Git which files to ignore. Create a <code>.gitignore</code> file in your repo root:</p>
<pre><code><span class="lang-label">.gitignore</span>
<span class="comment"># Dependencies</span>
node_modules/
venv/
__pycache__/
<span class="comment"># Environment variables (NEVER commit these)</span>
.env
.env.local
<span class="comment"># Build output</span>
dist/
build/
<span class="comment"># IDE files</span>
.vscode/
.idea/
<span class="comment"># OS files</span>
.DS_Store
Thumbs.db
<span class="comment"># Logs</span>
*.log</code></pre>
</section>
<!-- ============================================================ -->
<!-- SECTION 6: BRANCHES & POINTERS -->
<!-- ============================================================ -->
<section id="branches-pointers">
<h2>6. Branches & Pointers</h2>
<p>This is where Git's elegance shows. Once you understand this model, everything else makes sense.</p>
<h3>What Is a Branch?</h3>
<p>A branch is <strong>just a pointer</strong> (a 40-byte text file) that points to a commit. That's it. Creating a branch is instant because Git just creates a tiny file containing a commit hash.</p>
<div class="formula-box">
<pre>
A branch is just a pointer to a commit:
.git/refs/heads/main → contains: a1b2c3d
.git/refs/heads/feature-x → contains: e4f5g6h
That's literally all a branch is. A text file with a hash.
</pre>
</div>
<h3>HEAD: Where You Are Now</h3>
<p><strong>HEAD</strong> is a special pointer that tells Git "which branch (or commit) you're currently on." It usually points to a branch name, not directly to a commit.</p>
<div class="formula-box">
<pre>
Normal state (HEAD points to a branch):
HEAD → main → commit C
.git/HEAD contains: "ref: refs/heads/main"
Detached HEAD (HEAD points directly to a commit):
HEAD → commit B (not attached to any branch)
.git/HEAD contains: "a1b2c3d4e5f6..."
</pre>
</div>
<h3>Visual: How Branches Work</h3>
<p>Let's trace through creating a branch and making commits:</p>
<div class="formula-box">
<pre>
Step 1: You have 3 commits on main
═══════════════════════════════════
A ← B ← C
↑
main
↑
HEAD
(Each commit points BACK to its parent.
"main" points to the latest commit C.
HEAD points to main.)
Step 2: Create a new branch "feature"
══════════════════════════════════════
$ git branch feature
A ← B ← C
↑
main
feature
↑
HEAD (still on main)
(Both branches point to the same commit.
HEAD is still on main.)
Step 3: Switch to the feature branch
═════════════════════════════════════
$ git checkout feature
(or: git switch feature)
A ← B ← C
↑
main
feature
↑
HEAD (now on feature)
Step 4: Make a commit on feature
════════════════════════════════
A ← B ← C ← D
↑ ↑
main feature
↑
HEAD
(feature moved forward. main stayed put.)
Step 5: Switch back to main and commit
═══════════════════════════════════════
$ git checkout main
(edit files, commit)
← D
/ ↑
A ← B ← C feature
\
← E
↑
main
↑
HEAD
(History has DIVERGED. This is normal.
main and feature have different commits.)
</pre>
</div>
<div class="tip-box">
<div class="label">The DAG</div>
<p>Git's history is a <strong>Directed Acyclic Graph</strong> (DAG). Each commit points back to its parent(s). Branches are just movable pointers into this graph. When you merge, a commit gets two parents. The graph never has cycles -- you can't be your own ancestor.</p>
</div>
<h3>Branch Commands</h3>
<pre><code><span class="lang-label">Bash</span>
<span class="comment"># List branches</span>
git branch <span class="comment"># Local branches</span>
git branch -a <span class="comment"># All branches (local + remote)</span>
git branch -v <span class="comment"># Show last commit on each branch</span>
<span class="comment"># Create a branch</span>
git branch feature-x <span class="comment"># Create but don't switch</span>
git checkout -b feature-x <span class="comment"># Create AND switch (classic)</span>
git switch -c feature-x <span class="comment"># Create AND switch (modern)</span>
<span class="comment"># Switch branches</span>
git checkout main <span class="comment"># Classic</span>
git switch main <span class="comment"># Modern (preferred)</span>
<span class="comment"># Rename a branch</span>
git branch -m old-name new-name <span class="comment"># Rename any branch</span>
git branch -m new-name <span class="comment"># Rename current branch</span>
<span class="comment"># Delete a branch</span>
git branch -d feature-x <span class="comment"># Safe delete (only if merged)</span>
git branch -D feature-x <span class="comment"># Force delete (even if not merged)</span></code></pre>
<div class="warning-box">
<div class="label">Detached HEAD State</div>
<p>If you <code>git checkout abc123</code> (a commit hash instead of a branch name), you enter "detached HEAD" state. Any commits you make here will be orphaned when you switch away -- they're not on any branch. If you want to keep them, create a branch first: <code>git checkout -b my-branch</code>.</p>
</div>
</section>
<!-- ============================================================ -->
<!-- SECTION 7: MERGING -->
<!-- ============================================================ -->
<section id="merging">
<h2>7. Merging</h2>
<p>Merging combines the work from two branches. Git is smart about this -- it can usually figure out how to combine changes automatically.</p>
<h3>Fast-Forward Merge</h3>
<p>When the target branch hasn't diverged (no new commits since the branch was created), Git just moves the pointer forward. No new commit needed.</p>
<div class="formula-box">
<pre>
Before (main has no new commits since feature branched off):
A ← B ← C ← D ← E
↑ ↑
main feature
$ git checkout main
$ git merge feature
After (fast-forward -- main just moved forward):
A ← B ← C ← D ← E
↑
main
feature
No merge commit created. History is linear.
</pre>
</div>
<h3>Three-Way Merge</h3>
<p>When both branches have new commits (history has diverged), Git creates a <strong>merge commit</strong> with two parents.</p>
<div class="formula-box">
<pre>
Before (both branches have new commits):
← D ← E
/ ↑
A ← B ← C feature
\
← F ← G
↑
main
$ git checkout main
$ git merge feature
After (merge commit M has two parents):
← D ← E ──┐
/ ↑ \
A ← B ← C feature \
\ ↓
← F ← G ← ← ← M
↑
main
M is the merge commit. It has TWO parents: G and E.
</pre>
</div>
<h3>Merge Conflicts</h3>
<p>When both branches changed the <strong>same lines</strong> in the same file, Git can't auto-merge. You get a conflict:</p>
<pre><code><span class="lang-label">Bash</span>
$ git merge feature
<span class="comment"># CONFLICT (content): Merge conflict in src/auth.js</span>
<span class="comment"># Automatic merge failed; fix conflicts and then commit.</span></code></pre>
<p>Git marks the conflicting sections in the file:</p>
<pre><code><span class="lang-label">src/auth.js (with conflict markers)</span>
function login(user) {
<<<<<<< HEAD
return authenticate(user.email, user.password);
=======
return verifyCredentials(user.username, user.token);
>>>>>>> feature
}</code></pre>
<div class="formula-box">
<pre>
Reading conflict markers:
<<<<<<< HEAD
(your current branch's version)
=======
(the incoming branch's version)
>>>>>>> feature
To resolve:
1. Edit the file -- keep what you want, remove the markers
2. git add src/auth.js (mark as resolved)
3. git commit (finish the merge)
</pre>
</div>
<pre><code><span class="lang-label">Bash</span>
<span class="comment"># During a conflict, useful commands:</span>
git status <span class="comment"># Shows which files have conflicts</span>
git diff <span class="comment"># Shows the conflict details</span>
git merge --abort <span class="comment"># Abort the merge entirely, go back to before</span>
<span class="comment"># After manually fixing conflicts:</span>
git add src/auth.js <span class="comment"># Mark as resolved</span>
git commit <span class="comment"># Complete the merge</span></code></pre>
</section>
<!-- ============================================================ -->
<!-- SECTION 8: READING & NAVIGATING HISTORY -->
<!-- ============================================================ -->
<section id="history">
<h2>8. Reading & Navigating History</h2>
<h3>git log -- Your Time Machine</h3>
<pre><code><span class="lang-label">Bash</span>
<span class="comment"># Basic log</span>
git log
<span class="comment"># One-line compact view</span>
git log --oneline
<span class="comment"># Beautiful graph of ALL branches</span>
git log --oneline --graph --all --decorate
<span class="comment"># Show the last 5 commits</span>
git log -5
<span class="comment"># Log for a specific file</span>
git log --oneline -- src/auth.js
<span class="comment"># Search commits by message</span>
git log --grep=<span class="string">"fix login"</span>
<span class="comment"># Search commits by code change (pickaxe)</span>
git log -S <span class="string">"functionName"</span> <span class="comment"># Find when a string was added/removed</span>
<span class="comment"># Show who changed each line (blame)</span>
git blame src/auth.js
<span class="comment"># Show what a specific commit changed</span>
git show abc123f</code></pre>
<div class="example-box">
<div class="label">Example: git log --oneline --graph --all</div>
<pre>
* e4f5g6h (HEAD -> main) Merge feature into main
|\
| * c3d4e5f (feature) Add user dashboard
| * a1b2c3d Add auth middleware
|/
* 9f8e7d6 Initial setup
* 1a2b3c4 First commit
</pre>
</div>
<h3>Referring to Commits</h3>
<p>Git gives you many ways to refer to commits without typing full hashes:</p>
<table>
<thead>
<tr><th>Syntax</th><th>Meaning</th><th>Example</th></tr>
</thead>
<tbody>
<tr><td><code>HEAD</code></td><td>Current commit</td><td><code>git show HEAD</code></td></tr>
<tr><td><code>HEAD~1</code></td><td>One commit before HEAD</td><td><code>git show HEAD~1</code></td></tr>
<tr><td><code>HEAD~3</code></td><td>Three commits before HEAD</td><td><code>git diff HEAD~3</code></td></tr>
<tr><td><code>HEAD^</code></td><td>First parent of HEAD</td><td><code>git show HEAD^</code></td></tr>
<tr><td><code>HEAD^2</code></td><td>Second parent (merge commits only)</td><td><code>git show HEAD^2</code></td></tr>
<tr><td><code>main</code></td><td>Tip of main branch</td><td><code>git diff main</code></td></tr>
<tr><td><code>abc123f</code></td><td>Specific commit by hash</td><td><code>git show abc123f</code></td></tr>
<tr><td><code>v1.0</code></td><td>Tagged commit</td><td><code>git show v1.0</code></td></tr>
</tbody>
</table>
<div class="formula-box">
<pre>
~ vs ^ (they're different!):
~ means "go back N commits along the first parent"
^ means "choose which parent" (for merge commits)
HEAD~3 = great-grandparent (3 commits back)
HEAD^2 = second parent of HEAD (the merged branch)
Visual:
← D ← E (feature)
/ \
A ← B ← C M (merge commit, HEAD)
\ /
← F ← G
HEAD = M
HEAD^ = G (first parent -- the branch you were ON)
HEAD^2 = E (second parent -- the branch you MERGED)
HEAD~1 = G (same as HEAD^ for first parent)
HEAD~2 = F
HEAD~3 = C
</pre>
</div>
<h3>git reflog -- Your Safety Net</h3>
<p>The <strong>reflog</strong> records every single time HEAD moves. Even if you "lose" commits by resetting or deleting branches, the reflog remembers them for 90 days.</p>
<pre><code><span class="lang-label">Bash</span>
<span class="comment"># Show the reflog (every HEAD movement)</span>
git reflog
<span class="comment"># Output looks like:</span>
<span class="comment"># e4f5g6h HEAD@{0}: commit: Add dashboard</span>
<span class="comment"># a1b2c3d HEAD@{1}: checkout: moving from main to feature</span>
<span class="comment"># 9f8e7d6 HEAD@{2}: commit: Initial setup</span>
<span class="comment"># You can use reflog references to recover anything:</span>
git checkout HEAD@{3} <span class="comment"># Go back to where HEAD was 3 moves ago</span>
git branch recovery HEAD@{5} <span class="comment"># Create branch at an old position</span></code></pre>
<div class="tip-box">
<div class="label">The Reflog Saves Lives</div>
<p>Did a bad reset? Deleted a branch? Force-pushed and lost commits? <code>git reflog</code> shows you the hash of every state HEAD has been in. Find the hash of the state you want, then <code>git checkout -b rescue <hash></code> to recover.</p>
</div>
</section>
<!-- ============================================================ -->
<!-- SECTION 9: UNDOING THINGS -->
<!-- ============================================================ -->
<section id="undoing">
<h2>9. Undoing Things (Reset, Revert, Restore)</h2>
<p>There are many ways to undo in Git. The right one depends on what you want to undo and whether the change has been pushed.</p>
<h3>git restore -- Undo Working Directory Changes</h3>
<pre><code><span class="lang-label">Bash</span>
<span class="comment"># Discard changes in a file (go back to last committed version)</span>
git restore src/auth.js
<span class="comment"># Unstage a file (move from staging back to working directory)</span>
git restore --staged src/auth.js
<span class="comment"># Restore a file to a specific commit's version</span>
git restore --source=HEAD~3 src/auth.js</code></pre>