Learn the core Git workflow by creating repositories, staging changes, and making commits. Master the fundamental commands that form the foundation of all Git operations.
Now that Git is installed and configured, it's time to learn the essential commands you'll use every single day as a developer. The workflow of init, add, and commit forms the foundation of everything you do with Git.
Think of this workflow as taking photographs:
By the end of this lesson, you'll be creating repositories, staging changes, and making commits with confidence. These skills will become second nature as you practice.
A Git repository (or "repo") is a project folder that Git is tracking. The git init command creates a new repository or converts an existing folder into one.
Creating a New Repository:
1# Create a new project folder
2mkdir my-project
3cd my-project
4
5# Initialize Git repository
6git init
7
8# Output:
9# Initialized empty Git repository in /path/to/my-project/.git/What Happens When You Init:
Git creates a hidden .git folder containing:
1.git/
2├── HEAD # Points to current branch
3├── config # Repository configuration
4├── objects/ # All commit data stored here
5├── refs/ # Branch and tag references
6└── ... # Other Git internalsImportant: Never manually edit the .git folder unless you know exactly what you're doing!
Initializing in an Existing Folder:
1# Navigate to existing project
2cd existing-project
3
4# Initialize Git (safe - doesn't overwrite files)
5git initChecking Repository Status:
1# See the current state of your repository
2git status
3
4# Output for a new repo:
5# On branch main
6# No commits yet
7# nothing to commit (create/copy files and use "git add" to track)Common Beginner Mistake:
Initializing Git inside the wrong folder. Always verify your location:
1pwd # Print working directory
2ls -la # List all files including hidden .gitBefore learning add and commit, you must understand Git's three main areas:
1. Working Directory
2. Staging Area (Index)
3. Repository (.git directory)
1Working Directory Staging Area Repository
2 │ │ │
3 │ git add ──────► │ │
4 │ │ git commit ─►│
5 │◄──────────── git checkout ───────│Why Have a Staging Area?
The staging area gives you fine-grained control:
1# Scenario: You fixed a bug AND added a new feature
2# But you want separate commits for each
3
4# Stage only the bug fix file
5git add bugfix.js
6git commit -m "Fix login validation bug"
7
8# Then stage the feature file
9git add feature.js
10git commit -m "Add user preferences feature"File States:
| State | Meaning |
|-------|---------|
| Untracked | Git doesn't know about this file |
| Unmodified | File hasn't changed since last commit |
| Modified | File changed but not staged |
| Staged | File is ready for next commit |
Viewing States:
1git status
2
3# Changes to be committed: (staged)
4# new file: index.html
5#
6# Changes not staged for commit: (modified)
7# modified: app.js
8#
9# Untracked files: (untracked)
10# config.jsonThe git add command moves changes from the working directory to the staging area.
Basic Usage:
1# Add a specific file
2git add filename.js
3
4# Add multiple specific files
5git add file1.js file2.js file3.css
6
7# Add all files in a directory
8git add src/
9
10# Add all changed files (tracked and untracked)
11git add .
12
13# Add all tracked files that were modified
14git add -uPattern Matching:
1# Add all JavaScript files
2git add *.js
3
4# Add all files in any src subdirectory
5git add src/**/*
6
7# Add all TypeScript files recursively
8git add "**/*.ts"Interactive Staging:
1# Stage parts of a file (patch mode)
2git add -p filename.js
3
4# Git will show each change and ask:
5# Stage this hunk [y,n,q,a,d,s,e,?]?
6# y = yes, stage this chunk
7# n = no, skip this chunk
8# s = split into smaller chunks
9# q = quit, don't stage anything elseChecking What's Staged:
1# See what's staged vs unstaged
2git status
3
4# See the actual changes that are staged
5git diff --staged
6
7# See unstaged changes
8git diffUnstaging Changes:
1# Unstage a file (keep the changes in working directory)
2git restore --staged filename.js
3
4# Or the older way:
5git reset HEAD filename.jsBest Practices:
git diff to see what you're addinggit add . is convenient but review first.gitignore:** Prevent accidental staging of build files, secrets, etc.A commit is a snapshot of your staged changes with a message describing what changed and why.
Basic Commit:
1# Commit with a message
2git commit -m "Add user authentication feature"
3
4# Output:
5# [main 7a8b9c0] Add user authentication feature
6# 3 files changed, 127 insertions(+), 4 deletions(-)Commit with Detailed Message:
1# Opens your editor for a longer message
2git commit
3
4# In the editor, write:
5# Line 1: Short summary (50 chars or less)
6# Line 2: [blank]
7# Line 3+: Detailed description
8
9# Example:
10# Add user authentication feature
11#
12# - Implement JWT token generation
13# - Add login and logout endpoints
14# - Create password hashing utility
15# - Add rate limiting for login attempts
16#
17# Closes #42Shortcut: Stage and Commit Together:
1# Stage all TRACKED modified files and commit
2git commit -am "Fix typo in documentation"
3
4# Note: This won't add untracked files!Amending the Last Commit:
1# Forgot to stage a file? Add it to the last commit
2git add forgotten-file.js
3git commit --amend
4
5# Just want to change the message?
6git commit --amend -m "Better commit message"
7
8# Add to last commit, keep same message
9git commit --amend --no-edit⚠️ Warning: Never amend commits that have been pushed to a shared repository!
Commit Message Best Practices:
| Do | Don't |
|----|-------|
| "Fix login redirect bug" | "Fixed stuff" |
| "Add email validation" | "WIP" |
| "Refactor auth middleware" | "Changes" |
| Use imperative mood | Use past tense |
| Be specific | Be vague |
Conventional Commit Format:
1# Type: feat, fix, docs, style, refactor, test, chore
2git commit -m "feat: add user profile page"
3git commit -m "fix: resolve memory leak in data processing"
4git commit -m "docs: update API documentation"
5git commit -m "refactor: extract validation logic"The git log command shows your commit history.
Basic Log:
1git log
2
3# Output:
4# commit 7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b
5# Author: Your Name <[email protected]>
6# Date: Mon Jan 15 14:30:00 2024 -0500
7#
8# Add user authentication feature
9#
10# commit 1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b
11# ...Useful Log Formats:
1# One line per commit
2git log --oneline
3# 7a8b9c0 Add user authentication feature
4# 1a2b3c4 Fix homepage layout
5# 9d8e7f6 Initial commit
6
7# Show branch graph
8git log --graph --oneline --all
9
10# Show changes in each commit
11git log -p
12
13# Limit number of commits
14git log -5 # Last 5 commits
15
16# Search commit messages
17git log --grep="authentication"
18
19# Filter by author
20git log --author="John"
21
22# Filter by date
23git log --since="2024-01-01" --until="2024-01-31"
24
25# Filter by file
26git log -- filename.jsPrettier Log with Alias:
1# Set up a pretty log alias
2git config --global alias.lg "log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
3
4# Now use:
5git lgViewing Specific Commits:
1# Show details of a specific commit
2git show 7a8b9c0
3
4# Show what files changed in a commit
5git show --stat 7a8b9c0
6
7# Show just the message
8git log -1 --format="%B" 7a8b9c0Other Useful Commands:
1# Show who changed each line of a file
2git blame filename.js
3
4# Show a summary of changes
5git shortlog
6
7# Count commits by author
8git shortlog -snFollow along with this interactive demonstration of a complete Git workflow from init to multiple commits.
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Git Workflow Practice - Interactive Demo</title>
7 <style>
8 body {
9 font-family: 'Segoe UI', system-ui, sans-serif;
10 background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
11 min-height: 100vh;
12 color: #e0e0e0;
13 padding: 20px;
14 }
15 .container { max-width: 1000px; margin: 0 auto; }
16 h1 { text-align: center; color: #f97316; margin-bottom: 10px; }
17 .subtitle { text-align: center; color: #9ca3af; margin-bottom: 30px; }
18
19 .terminal {
20 background: #0f172a;
21 border-radius: 12px;
22 overflow: hidden;
23 margin-bottom: 20px;
24 }
25 .terminal-header {
26 background: #1e293b;
27 padding: 10px 15px;
28 display: flex;
29 gap: 8px;
30 }
31 .terminal-dot {
32 width: 12px;
33 height: 12px;
34 border-radius: 50%;
35 }
36 .dot-red { background: #ef4444; }
37 .dot-yellow { background: #eab308; }
38 .dot-green { background: #22c55e; }
39 .terminal-body {
40 padding: 20px;
41 font-family: 'Fira Code', monospace;
42 font-size: 14px;
43 max-height: 300px;
44 overflow-y: auto;
45 }
46 .prompt { color: #22c55e; }
47 .command { color: #60a5fa; }
48 .output { color: #9ca3af; margin-left: 20px; }
49 .success { color: #4ade80; }
50 .info { color: #fbbf24; }
51
52 .workflow-buttons {
53 display: flex;
54 flex-wrap: wrap;
55 gap: 10px;
56 margin-bottom: 20px;
57 }
58 .workflow-btn {
59 background: linear-gradient(135deg, #3b82f6, #2563eb);
60 border: none;
61 padding: 12px 24px;
62 border-radius: 8px;
63 color: white;
64 cursor: pointer;
65 font-weight: 500;
66 transition: all 0.3s;
67 }
68 .workflow-btn:hover:not(:disabled) {
69 transform: translateY(-2px);
70 box-shadow: 0 5px 15px rgba(59, 130, 246, 0.4);
71 }
72 .workflow-btn:disabled {
73 opacity: 0.5;
74 cursor: not-allowed;
75 }
76 .workflow-btn.completed {
77 background: linear-gradient(135deg, #22c55e, #16a34a);
78 }
79
80 .file-tree {
81 background: rgba(255, 255, 255, 0.03);
82 border-radius: 12px;
83 padding: 20px;
84 margin-bottom: 20px;
85 }
86 .file-tree h3 { color: #f97316; margin-bottom: 15px; }
87 .tree-item {
88 padding: 5px 0;
89 padding-left: 20px;
90 font-family: monospace;
91 }
92 .folder { color: #60a5fa; }
93 .file { color: #e0e0e0; }
94 .file-new { color: #22c55e; }
95 .file-modified { color: #fbbf24; }
96 .file-staged { color: #a78bfa; }
97
98 .status-panel {
99 background: rgba(255, 255, 255, 0.03);
100 border-radius: 12px;
101 padding: 20px;
102 }
103 .status-panel h3 { color: #22c55e; margin-bottom: 15px; }
104 .status-item {
105 display: flex;
106 justify-content: space-between;
107 padding: 10px 0;
108 border-bottom: 1px solid rgba(255, 255, 255, 0.05);
109 }
110 .status-label { color: #9ca3af; }
111 .status-value { color: #e0e0e0; font-weight: 600; }
112
113 .reset-btn {
114 background: #ef4444;
115 border: none;
116 padding: 10px 20px;
117 border-radius: 8px;
118 color: white;
119 cursor: pointer;
120 margin-top: 20px;
121 }
122 </style>
123</head>
124<body>
125 <div class="container">
126 <h1>Git Workflow Practice</h1>
127 <p class="subtitle">Step through a complete Git workflow interactively</p>
128
129 <div class="workflow-buttons" id="buttons">
130 <button class="workflow-btn" onclick="runStep(0)">1. git init</button>
131 <button class="workflow-btn" disabled onclick="runStep(1)">2. Create files</button>
132 <button class="workflow-btn" disabled onclick="runStep(2)">3. git status</button>
133 <button class="workflow-btn" disabled onclick="runStep(3)">4. git add</button>
134 <button class="workflow-btn" disabled onclick="runStep(4)">5. git commit</button>
135 <button class="workflow-btn" disabled onclick="runStep(5)">6. Modify file</button>
136 <button class="workflow-btn" disabled onclick="runStep(6)">7. git diff</button>
137 <button class="workflow-btn" disabled onclick="runStep(7)">8. git commit -am</button>
138 <button class="workflow-btn" disabled onclick="runStep(8)">9. git log</button>
139 </div>
140
141 <div class="terminal">
142 <div class="terminal-header">
143 <div class="terminal-dot dot-red"></div>
144 <div class="terminal-dot dot-yellow"></div>
145 <div class="terminal-dot dot-green"></div>
146 </div>
147 <div class="terminal-body" id="terminal">
148 <div><span class="prompt">~/projects $</span> <span class="info">Click "1. git init" to begin</span></div>
149 </div>
150 </div>
151
152 <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px;">
153 <div class="file-tree">
154 <h3>Project Files</h3>
155 <div id="file-tree">
156 <div class="tree-item folder">my-project/</div>
157 <div class="tree-item" style="color: #6b7280;">(empty)</div>
158 </div>
159 </div>
160
161 <div class="status-panel">
162 <h3>Repository Status</h3>
163 <div class="status-item">
164 <span class="status-label">Git Initialized:</span>
165 <span class="status-value" id="init-status">No</span>
166 </div>
167 <div class="status-item">
168 <span class="status-label">Staged Files:</span>
169 <span class="status-value" id="staged-count">0</span>
170 </div>
171 <div class="status-item">
172 <span class="status-label">Total Commits:</span>
173 <span class="status-value" id="commit-count">0</span>
174 </div>
175 <div class="status-item">
176 <span class="status-label">Current Branch:</span>
177 <span class="status-value" id="branch-name">-</span>
178 </div>
179 </div>
180 </div>
181
182 <button class="reset-btn" onclick="resetDemo()">Reset Demo</button>
183 </div>
184
185 <script>
186 let currentStep = 0;
187 let state = {
188 initialized: false,
189 files: [],
190 staged: [],
191 commits: 0
192 };
193
194 const steps = [
195 {
196 command: 'git init',
197 output: 'Initialized empty Git repository in ~/projects/my-project/.git/',
198 action: () => {
199 state.initialized = true;
200 document.getElementById('init-status').textContent = 'Yes';
201 document.getElementById('branch-name').textContent = 'main';
202 updateFileTree();
203 }
204 },
205 {
206 command: '# Create project files',
207 output: 'touch index.html app.js styles.css',
208 action: () => {
209 state.files = ['index.html', 'app.js', 'styles.css'];
210 updateFileTree();
211 }
212 },
213 {
214 command: 'git status',
215 output: `On branch main
216
217No commits yet
218
219Untracked files:
220 (use "git add <file>..." to include in what will be committed)
221 <span class="file-new">index.html</span>
222 <span class="file-new">app.js</span>
223 <span class="file-new">styles.css</span>
224
225nothing added to commit but untracked files present`,
226 action: () => {}
227 },
228 {
229 command: 'git add .',
230 output: '# All files staged for commit',
231 action: () => {
232 state.staged = [...state.files];
233 document.getElementById('staged-count').textContent = state.staged.length;
234 updateFileTree();
235 }
236 },
237 {
238 command: 'git commit -m "Initial commit: Add project structure"',
239 output: `[main (root-commit) a1b2c3d] Initial commit: Add project structure
240 3 files changed, 0 insertions(+), 0 deletions(-)
241 create mode 100644 index.html
242 create mode 100644 app.js
243 create mode 100644 styles.css`,
244 action: () => {
245 state.staged = [];
246 state.commits = 1;
247 document.getElementById('staged-count').textContent = '0';
248 document.getElementById('commit-count').textContent = '1';
249 updateFileTree();
250 }
251 },
252 {
253 command: '# Edit app.js - add console.log',
254 output: 'echo "console.log(\'Hello Git!\');" >> app.js',
255 action: () => {
256 state.modified = ['app.js'];
257 updateFileTree();
258 }
259 },
260 {
261 command: 'git diff',
262 output: `<span style="color: #f97316;">diff --git a/app.js b/app.js</span>
263index e69de29..8b13789 100644
264--- a/app.js
265+++ b/app.js
266@@ -0,0 +1 @@
267<span style="color: #22c55e;">+console.log('Hello Git!');</span>`,
268 action: () => {}
269 },
270 {
271 command: 'git commit -am "Add greeting message to app"',
272 output: `[main d4e5f6g] Add greeting message to app
273 1 file changed, 1 insertion(+)`,
274 action: () => {
275 state.modified = [];
276 state.commits = 2;
277 document.getElementById('commit-count').textContent = '2';
278 updateFileTree();
279 }
280 },
281 {
282 command: 'git log --oneline',
283 output: `<span style="color: #f97316;">d4e5f6g</span> Add greeting message to app
284<span style="color: #f97316;">a1b2c3d</span> Initial commit: Add project structure`,
285 action: () => {}
286 }
287 ];
288
289 function runStep(step) {
290 if (step !== currentStep) return;
291
292 const terminal = document.getElementById('terminal');
293 const s = steps[step];
294
295 terminal.innerHTML += `301`;
302
303 s.action();
304
305 // Mark button as completed
306 const buttons = document.querySelectorAll('.workflow-btn');
307 buttons[step].classList.add('completed');
308 buttons[step].disabled = true;
309
310 // Enable next button
311 if (step + 1 < buttons.length) {
312 buttons[step + 1].disabled = false;
313 }
314
315 currentStep++;
316 terminal.scrollTop = terminal.scrollHeight;
317 }
318
319 function updateFileTree() {
320 const tree = document.getElementById('file-tree');
321 let html = '<div class="tree-item folder">my-project/</div>';
322
323 if (state.initialized) {
324 html += '<div class="tree-item folder" style="padding-left: 40px;">.git/</div>';
325 }
326
327 state.files.forEach(file => {
328 let className = 'file';
329 if (state.staged.includes(file)) {
330 className = 'file-staged';
331 } else if (state.modified && state.modified.includes(file)) {
332 className = 'file-modified';
333 } else if (!state.commits) {
334 className = 'file-new';
335 }
336 html += `<div class="tree-item ${className}" style="padding-left: 40px;">${file}</div>`;
337 });
338
339 tree.innerHTML = html;
340 }
341
342 function resetDemo() {
343 currentStep = 0;
344 state = { initialized: false, files: [], staged: [], commits: 0 };
345
346 document.getElementById('terminal').innerHTML = '<div><span class="prompt">~/projects $</span> <span class="info">Click "1. git init" to begin</span></div>';
347 document.getElementById('init-status').textContent = 'No';
348 document.getElementById('staged-count').textContent = '0';
349 document.getElementById('commit-count').textContent = '0';
350 document.getElementById('branch-name').textContent = '-';
351
352 const buttons = document.querySelectorAll('.workflow-btn');
353 buttons.forEach((btn, i) => {
354 btn.classList.remove('completed');
355 btn.disabled = i !== 0;
356 });
357
358 updateFileTree();
359 }
360 </script>
361</body>
362</html>This interactive demonstration walks you through a complete Git workflow:
Steps Covered:
Key Observations:
You now know the core Git workflow! In the next lesson, we'll connect to the wider world by learning about remotes. You'll clone repositories from GitHub, push your code to share with others, and pull changes from collaborators. Get ready to join millions of developers sharing code globally!
Your progress is saved in this browser, on this device only.