1. Courses
  2. Developer Foundations
  3. Git from the Inside
  4. Stop 2

35 minBeginner

Everyday Git: status, diff, log and good commits

Most of a developer's Git day is four questions: what did I change, what am I about to commit, what happened before, and who changed this and why. This lesson gives you the commands that answer each one, and the habits that make the answers useful months later.

By the end you can

  • Read a diff, and know exactly what git diff and git diff --staged each compare
  • Stage part of a file with git add -p, so one commit holds one change
  • Search history with git log options, git show and the pickaxe (-S)
  • Write a .gitignore that keeps secrets, dependencies and build output out of the repo
  • Write Conventional Commit messages, and check them automatically with a small linter

The everyday picture

Many stores keep a shop diary at the manager's desk. Each entry is short: "14 Sep, 10:12, moved rice to aisle 2 because the delivery door blocks aisle 6. Signed, shift manager." Months later, when head office asks why rice is in aisle 2, the answer is on one line, with a date and a name.

A bad diary says "changes made" every day. It is still a diary, but it answers nothing. Git history is the team's shop diary. Each commit is an entry, and the commit message is the line someone will read in six months while trying to understand a strange discount. The commands in this lesson read the diary; the habits make it worth reading.

Reading a diff

A is how Git shows a change. Git computes it on demand by comparing two snapshots (Snapshots and the three areas). Here is one real diff from this lesson's project, the kind you will read in every pull request:

Diff
diff --git a/src/discount.js b/src/discount.js
index 8e64b13..8bfea70 100644
--- a/src/discount.js
+++ b/src/discount.js
@@ -3,7 +3,7 @@ const MEMBER_RATE = 0.07; // 7% off for loyalty members (festival offer)
 const MAX_DISCOUNT_CENTS = 2500; // never more than $25 per order

 export function discountCents(totalCents, isMember) {
-  if (!isMember) return 0;
+  if (!isMember || !isEligible(totalCents)) return 0;
   return Math.min(Math.round(totalCents * MEMBER_RATE), MAX_DISCOUNT_CENTS);
 }
 

The first four lines name the file: a/ is the old version, b/ the new one, and the index line gives the short IDs of the two stored file contents. The @@ -3,7 +3,7 @@ line starts a : in the old file, 7 lines starting at line 3; in the new file, also 7 lines starting at line 3. After it Git repeats the nearest line above that looks like a function or a declaration, to help you see where you are. Lines starting with - were removed, + were added, and lines starting with a space are unchanged context, three above and below by default. A changed line shows as one removal plus one addition. VS Code shows the same thing side by side in its diff editor, with red and green.

Quick check

A hunk header reads @@ -10,6 +10,8 @@. What happened in that hunk?

git diff and git diff --staged compare different things

This is the single most useful fact about diffs, and the one most people get wrong. Remember the three areas from Snapshots and the three areas:

  • git diff compares the working folder with the staging area. It shows what you changed but have not staged. After git add, those lines disappear from it.
  • git diff --staged (same as --cached) compares the staging area with the last commit. It shows exactly what git commit would save now.
  • git diff HEAD compares the working folder with the last commit: everything, staged or not.

So the habit before every commit is git diff --staged. It is the only command that shows exactly what goes into the commit. Add --stat to any of them for a one-line-per-file summary instead of the full lines.

Quick check

You staged a fix in src/cart.js, then edited the same file again to try an idea. Which command shows only the new, unstaged idea?

Working folder what you see Staging area the next commit Last commit HEAD git diff not staged yet git diff --staged what commit saves git diff HEAD everything
Each diff command compares two neighbouring places. git diff shows edits you have not staged; git diff --staged shows exactly what the next commit will save; git diff HEAD shows both together. After git add, a change moves from the first to the second.

Staging part of a file with git add -p

Real work rarely fits one commit. You fix a bug and, in the same file, add a debug console.log. git add -p (patch mode) walks through each hunk and asks Stage this hunk [y,n,q,a,d,...]?. The keys you need: y stage this hunk, n skip it, s split it into smaller hunks when two changes are close together, q quit, ? help. The result is a file that is partly staged, shown as MM in git status -s, and a commit that contains only the fix.

VS Code does the same with the mouse. In the Source Control view, click a changed file to open the diff editor, select the lines, and use Git: Stage Selected Ranges from the Command Palette or the stage button in the diff editor's gutter. Many developers learn add -p anyway, because it makes you read every hunk before it goes in, and that is where stray debug lines and passwords get caught.

Reading history: log, show and the pickaxe

git log lists commits newest first. Its options turn it into a search tool:

  • --oneline: one line per commit, short ID and subject. Add --graph --all to draw branches as lines, which matters from Branches and merging on.
  • -p: show the diff of each commit. --stat shows only the file list.
  • --author="Dev Two", --since="2026-09-20", --until=...: filter by who and when.
  • -- src/discount.js: only commits that touched that file. The -- separates paths from options.
  • --format='%h %an %ad %s' --date=short: your own columns: short ID, author, date, subject.

git show <commit> prints one commit with its message and diff. git show <commit>:path prints a file as it was then.

The most powerful search is the : git log -S "0.07" lists only the commits where the number of times 0.07 appears in a file changed. That is, the commits that added or removed it. Add -p to see the change itself. -G "MEMBER_RATE" is the regular-expression cousin: commits whose diff has a changed line matching the pattern, even if the count stayed the same. And git blame -L 2,2 file shows who last changed line 2, in which commit and when. For a product manager, this is how "when did the member discount become 7%, and why?" gets answered in one minute instead of a meeting. Reading code like a detective takes this much further.

What never goes in: .gitignore

Some files must never be committed. A .gitignore file lists patterns, and Git stops showing matching files as untracked, so git add . skips them.

  • .env and other secret files. Anything committed is copied to every clone, and to GitHub when you push. Deleting it in a later commit does not remove it from history; the old commit still holds it. A password that was ever pushed must be treated as leaked and changed. Commit a .env.example with the variable names and fake values instead.
  • node_modules/. Often hundreds of megabytes, different per operating system, and fully recreated by npm install from package.json and the lockfile (Node.js and npm). Commit the lockfile, never the folder.
  • Build output and logs (dist/, coverage/, *.log): generated from the source, so they cause noisy diffs and merge conflicts.
  • OS and editor files (.DS_Store, Thumbs.db, personal .vscode files, as in VS Code for teams).

Patterns: dist/ matches a folder anywhere, *.log any file ending in .log, a leading ! makes an exception (!.env.example). Later lines win, so an exception must come after the rule it overrides. One trap: .gitignore only affects untracked files. If .env was already committed, adding it to .gitignore changes nothing; you must also run git rm --cached .env, which stops tracking it but keeps your local copy. git check-ignore -v file tells you which rule ignores a file. GitHub keeps good starting templates at github.com/github/gitignore.

Quick check

Your .gitignore has the line .env.*, which also hides .env.example. How do you keep .env.example in the repo?

Good commits: small, focused, well described

A good commit does one thing, completely. "Fix the eligibility check" is one commit; "fix eligibility, rename variables, update the README, bump a library" is four. Small commits are easier to review, safer to revert (Undo anything) and much easier to search. A useful test: if the subject line needs the word "and", it is probably two commits.

Many teams write messages in the format: type(scope): description. The specification itself defines feat (a new feature) and fix (a bug fix). The widely used Angular-based convention adds docs, style, refactor, perf, test, build, ci and chore. A ! after the type, or a BREAKING CHANGE: footer, marks a breaking change. Because the format is machine-readable, tools can generate changelogs and version numbers from it: fix means a patch release, feat a minor one, a breaking change a major one (Branching strategies, releases and versions).

Write the subject in the imperative, as if completing "this commit will...": fix: no member discount on orders below $30. Keep it to about 50 characters; VS Code's commit box warns past that if you turn on the git.inputValidation setting. If the why is not obvious, add a blank line and a body: "Finance asked for this on 5 Oct because small baskets were losing money." Code shows what changed; only the message can say why.

Quick check

Which subject line follows Conventional Commits and the imperative style?

Hard to use Easy to use a91f0c2 update 5d02e7b fixes and changes e33b9a1 wip 0c7f4d8 final changes 2 chore: ignore secrets feat(receipt): show discount line fix: no discount below $30 feat: member rate 7% for festival one commit mixes a fix, a rename and debug logs one change per commit, the body says why Why no small-order discount? left: read 4 diffs right: 1 commit
The same week of work twice. On the left, vague messages and mixed commits: to find out why small orders lost their discount, you read every diff. On the right, one commit per change with a Conventional Commit subject: git log --oneline answers the question, and each commit can be reverted alone.

Build it

A realistic session on a small discounts repo for the Partner app: change two files, stage one hunk, commit with a Conventional Commit, write a .gitignore, then search history. All output is from Git 2.55. The Practice section afterwards has a commit-message linter to write and test in your browser.

  1. Start from a repo with some history

    Three earlier commits by three people already exist. If you are following along, make a similar repo with your own commits, or clone any small project. Money is in integer cents throughout.

    Terminal
    cd ~/work/discounts
    git log --oneline
    Output
    e4a33d3 (HEAD -> main) feat: raise member discount to 7% for the festival
    84aca99 feat: add dollar formatter for receipts
    170f204 feat: add member discount with $25 cap
  2. Make two changes in one file and one in another

    In src/discount.js, apply the eligibility check (the real fix) and add a debug line you do not want to commit. In src/receipt.js, add a new function.

    JavaScript
    // src/discount.js (the two edits)
      if (!isMember || !isEligible(totalCents)) return 0;   // the fix
    ...
    export function isEligible(totalCents) {
      console.log('isEligible', totalCents); // TODO remove   // debug only
      return totalCents >= 3000;
    }
    
    // src/receipt.js (added at the end)
    export function discountLine(cents) {
      return `Member discount: -${dollars(cents)}`;
    }
    Terminal
    git status -s
    git diff --stat
    Output
     M src/discount.js
     M src/receipt.js
     src/discount.js | 3 ++-
     src/receipt.js  | 4 ++++
     2 files changed, 6 insertions(+), 1 deletion(-)
  3. Stage only the fix with git add -p

    Git shows each hunk of discount.js and waits for a key. Answer y for the first hunk (the fix) and n for the second (the debug line).

    Terminal
    git add -p src/discount.js
    Output
    @@ -3,7 +3,7 @@ const MEMBER_RATE = 0.07; // 7% off for loyalty members (festival offer)
     const MAX_DISCOUNT_CENTS = 2500; // never more than $25 per order
    
     export function discountCents(totalCents, isMember) {
    -  if (!isMember) return 0;
    +  if (!isMember || !isEligible(totalCents)) return 0;
       return Math.min(Math.round(totalCents * MEMBER_RATE), MAX_DISCOUNT_CENTS);
     }
    
    (1/2) Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,e,p,P,?]? y
    @@ -12,5 +12,6 @@ export function discountCents(totalCents, isMember) {
      * This protects the margin on small baskets.
      */
     export function isEligible(totalCents) {
    +  console.log('isEligible', totalCents); // TODO remove
       return totalCents >= 3000;
     }
    (2/2) Stage this hunk [y,n,q,a,d,K,J,g,/,e,p,P,?]? n

    The first lines of the output, the diff --git header, are left out here. If two changes sit in one hunk, press s to split it.

  4. Check what you are about to commit

    Compare the two diffs. The staged one has only the fix; the unstaged one has the debug line and the receipt change.

    Terminal
    git status -s
    git diff --staged --stat
    git diff --stat
    Output
    MM src/discount.js
     M src/receipt.js
     src/discount.js | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
     src/discount.js | 1 +
     src/receipt.js  | 4 ++++
     2 files changed, 5 insertions(+)
  5. Commit the fix, drop the debug line, commit the receipt

    Commit the fix with a Conventional Commit subject. The single quotes matter: inside double quotes the shell would treat $30 as a variable and drop it. Then throw away the debug line with git restore (it is unstaged, so this is safe for the committed fix) and commit the receipt change separately. Two changes, two commits.

    Terminal
    git commit -m 'fix: no member discount on orders below $30'
    git restore src/discount.js
    git add src/receipt.js
    git commit -m "feat: show member discount line on receipt"
    git status -s
    git log --oneline
    Output
    [main dbf1e0d] fix: no member discount on orders below $30
     1 file changed, 1 insertion(+), 1 deletion(-)
    [main 0b081c7] feat: show member discount line on receipt
     1 file changed, 4 insertions(+)
    0b081c7 (HEAD -> main) feat: show member discount line on receipt
    dbf1e0d fix: no member discount on orders below $30
    e4a33d3 feat: raise member discount to 7% for the festival
    84aca99 feat: add dollar formatter for receipts
    170f204 feat: add member discount with $25 cap

    git status -s printed nothing: the working folder is clean. For a message with a body, run git commit without -m; VS Code opens (thanks to core.editor from Snapshots and the three areas), you write the subject, a blank line and the body, then save and close the tab.

  6. Write a .gitignore for a Node project

    Create the files a real project accumulates, and look at what Git would offer to commit.

    Terminal
    git status -s
    Output
    ?? .env
    ?? dist/
    ?? logs/
    ?? node_modules/

    Every one of these must stay out. Create .gitignore and a safe .env.example:

    Output
    # dependencies: npm install recreates them
    node_modules/
    
    # build output and test coverage
    dist/
    coverage/
    
    # logs
    *.log
    logs/
    
    # secrets: never commit them; commit .env.example instead
    .env
    .env.*
    !.env.example
    
    # editor and OS files
    .vscode/*
    !.vscode/settings.json
    !.vscode/extensions.json
    .DS_Store
    Thumbs.db
    Terminal
    git status -s
    git check-ignore -v .env dist/main.js node_modules/left-pad/index.js
    git add .gitignore .env.example
    git commit -m "chore: ignore secrets, dependencies and build output"
    Output
    ?? .env.example
    ?? .gitignore
    .gitignore:13:.env	.env
    .gitignore:5:dist/	dist/main.js
    .gitignore:2:node_modules/	node_modules/left-pad/index.js
    [main 7866e0a] chore: ignore secrets, dependencies and build output
     2 files changed, 24 insertions(+)
     create mode 100644 .env.example
     create mode 100644 .gitignore

    check-ignore -v prints the file, line and pattern that ignored each path. If a file you expected to be ignored is missing from its output, it is either tracked already (see the warning above) or not matched.

  7. Search history with log options

    Terminal
    git log --oneline --author="Dev Two"
    git log --oneline --since="2026-09-20" --until="2026-10-03"
    git log --format='%h %an %ad %s' --date=short
    Output
    e4a33d3 feat: raise member discount to 7% for the festival
    e4a33d3 feat: raise member discount to 7% for the festival
    84aca99 feat: add dollar formatter for receipts
    7866e0a Your Name 2026-10-07 chore: ignore secrets, dependencies and build output
    0b081c7 Your Name 2026-10-07 feat: show member discount line on receipt
    dbf1e0d Your Name 2026-10-07 fix: no member discount on orders below $30
    e4a33d3 Dev Two 2026-10-02 feat: raise member discount to 7% for the festival
    84aca99 Your Name 2026-09-28 feat: add dollar formatter for receipts
    170f204 Dev One 2026-09-14 feat: add member discount with $25 cap

    git log --oneline --graph --all shows the same list with a line of * marks on the left; with one branch it is a straight line, and from Branches and merging it shows branches splitting and joining.

  8. Find who changed a value, and when

    The question: when did the member rate become 7%? The pickaxe finds the commit that added the text 0.07; -p shows its diff, and git blame confirms who last touched line 2.

    Terminal
    git log -p -S "0.07" -- src/discount.js
    git blame -L 2,2 src/discount.js
    Output
    commit e4a33d393f009a99e1628c5ff161d8c648bb9ed6
    Author: Dev Two <dev2@example.com>
    Date:   Fri Oct 2 18:20:00 2026 +0600
    
        feat: raise member discount to 7% for the festival
    
    diff --git a/src/discount.js b/src/discount.js
    index 7fb4550..8e64b13 100644
    --- a/src/discount.js
    +++ b/src/discount.js
    @@ -1,5 +1,5 @@
     // discount.js: discount rules for Partner app orders
    -const MEMBER_RATE = 0.05; // 5% off for loyalty members
    +const MEMBER_RATE = 0.07; // 7% off for loyalty members (festival offer)
     const MAX_DISCOUNT_CENTS = 2500; // never more than $25 per order
    
     export function discountCents(totalCents, isMember) {
    e4a33d39 (Dev Two 2026-10-02 18:20:00 +0600 2) const MEMBER_RATE = 0.07; // 7% off for loyalty members (festival offer)

    Blame only shows the last change to a line, so a later reformat would hide the real author; the pickaxe looks through all of history. In VS Code, the Status Bar shows blame for the current line, and the Timeline view in the Explorer lists every commit that touched the open file. The free GitLens extension (eamodio.gitlens) adds richer blame and history views; it is popular, and it is still an extension, so apply the checks from VS Code essentials.

QuestionCommandIn VS Code
What did I change but not stage?git diffSource Control view, Changes, click a file
What will this commit contain?git diff --stagedSource Control view, Staged Changes
Stage part of a filegit add -pGit: Stage Selected Ranges
What happened recently?git log --oneline --graph --allSource Control Graph
What did one commit change?git show <id>Click the commit in the graph
Who last changed this line?git blame -L 2,2 fileStatus Bar blame
When did this text appear?git log -S "text" -p(no built-in view)
Why is this file ignored?git check-ignore -v fileGreyed out in the Explorer

Bring it to your team

You can check most of these by reading a PR's commit list, without reading the code.

  • "Can each commit in this PR be described in one line without the word 'and'?"
  • "Do our commit messages explain why, at least in the body, for anything a future reader could not guess?"
  • "Is .env in .gitignore in every repo, and do we have a .env.example that lists every variable?"
  • "If I ask when this business rule changed, can someone answer from git log in a minute?"
  • "Do we use Conventional Commits, and does a check enforce it, so the changelog and version number can be generated?"

Red flags

  • Subjects like "update", "fix", "changes" or "wip" in the main branch history.
  • node_modules, dist or a .env file appearing in a diff.
  • Leftover console.log lines and commented-out code in commits.
  • One commit per day containing everything that changed that day.

Practice

Write a commit-message linter

Teams often run a small check that rejects messages that do not follow the agreed format. Write lintCommit(message). It returns an array of problem codes, in this order, or an empty array when the message is fine:

  • 'type': the first line does not start with a known type (feat, fix, docs, style, refactor, perf, test, build, ci, chore), an optional scope in brackets, an optional !, then a colon, a space and a description.
  • 'too-long': the first line is longer than 72 characters.
  • 'period': the first line ends with a full stop.
  • 'blank-line': there is a second line and it is not empty (the body must be separated by a blank line).
JavaScript exerciseRuns in your browser with tests
const TYPES = ['feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'build', 'ci', 'chore'];

function lintCommit(message) {
  const errors = [];
  const lines = message.split('\n');
  const subject = lines[0];
  // check the subject, then the line after it
  return errors;
}
Ctrl + Enter runs
Show a solution
JavaScript
const TYPES = ['feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'build', 'ci', 'chore'];

function lintCommit(message) {
  const errors = [];
  const lines = message.split('\n');
  const subject = lines[0];
  // type, optional (scope), optional !, then ": " and a description
  const m = subject.match(/^([a-z]+)(\([a-z0-9-]+\))?!?: \S/);
  if (!m || !TYPES.includes(m[1])) errors.push('type');
  if (subject.length > 72) errors.push('too-long');
  if (subject.endsWith('.')) errors.push('period');
  if (lines.length > 1 && lines[1] !== '') errors.push('blank-line');
  return errors;
}
Pass the exercise to finish this task.

Rewrite a bad history

Here are four real-looking commits from the left side of the second diagram, with what each one actually did. Write a Conventional Commit subject for each, and say which one should have been split into two commits.

Output
update             changed the receipt footer text to "Thank you"
fixes and changes  fixed VAT rounding on receipts AND renamed calcTotal to cartTotal
wip                added a test for the 7% member rate
final changes 2    removed node_modules from the repo and added it to .gitignore
  • Every subject has a type from the list, a colon and an imperative description
  • The second commit is split into a fix and a refactor
  • The test commit uses test, and the node_modules commit uses chore (or build)
  • No subject contains "and", ends with a full stop, or is longer than about 50 characters
Show a model answer
Output
feat(receipt): thank the customer in the footer
fix(receipt): round VAT to whole cents
refactor: rename calcTotal to cartTotal
test: cover the 7% member rate
chore: stop tracking node_modules

"fixes and changes" did two unrelated things, so it becomes two commits: the fix can now be reverted or cherry-picked without the rename. The footer change could also be style or chore if your team does not treat copy changes as features; what matters is that the team agrees.

Check yourself

You ran git add src/discount.js. Now git diff prints nothing. Does that mean you have no changes?

A developer committed .env with a real API key and pushed it. The next day he adds .env to .gitignore. What is true now?

Which command answers "which commit first introduced the value 0.07 in discount.js?" most directly?

Flashcards

Open my review

Key takeaways

  • git diff shows unstaged edits; git diff --staged shows exactly what the next commit will save. Read the second one before every commit.
  • git add -p (or Stage Selected Ranges) puts only the hunks you choose into a commit, and makes you read each one.
  • git log with --oneline, --author, --since, -p and -S turns history into a searchable record; git blame shows the last change per line.
  • .gitignore keeps .env, node_modules and build output out, but only for untracked files. A pushed secret is leaked.
  • One change per commit, with a Conventional Commit subject in the imperative and a body that says why.
Episode transcriptThe shop diary nobody reads · about 10 minutes · tap any line to play from there
  1. NadiaImran, finance calls on a Monday. Member discounts in the Partner app went up last week. Who changed it, was it approved, and does it end after the festival rush?
  2. ImranIn most teams, that's an afternoon of searching chat messages.
  3. NadiaWith a good Git history, it's one command and about a minute. Today is everyday Git: reading changes, choosing what to commit, and searching the past.
  4. NadiaThe lesson's picture is a shop diary at the manager's desk. Each entry is short. Fourteenth of September, moved rice to aisle two because the delivery door blocks aisle six. Signed, shift manager.
  5. ImranAnd months later, when head office asks why rice is in aisle two, the answer is one line, with a date and a name.
  6. NadiaExactly. And a bad diary says changes made, every single day. It's still a diary. It just answers nothing. Git history is the team's diary, and every commit message is an entry.
  7. ImranStart with reading changes. Developers show me diffs, and my eyes glaze over.
  8. NadiaA diff has a simple shape. A header names the file. Then a line with two at signs starts a block of changes called a hunk. Lines with a minus were removed. Lines with a plus were added. Lines with a space are unchanged context, three above and three below.
  9. ImranAnd a changed line?
  10. NadiaShows as one minus and one plus. VS Code draws the same thing side by side, in red and green.
  11. ImranNow, my team has two diff commands. Git diff, and git diff dash dash staged. I always thought they were the same.
  12. NadiaThat's the most common mistake about diffs. They compare different places. Remember the three areas from last time.
  13. NadiaThe first diagram shows them stacked. If you can see it, three boxes, top to bottom: working folder, staging area, last commit. If not, picture three shelves again.
  14. NadiaPlain git diff compares the top two: the working folder with the staging area. So it shows what you changed but haven't staged. Git diff staged compares the bottom two: what you staged, against the last commit.
  15. ImranSo after I run git add, my change disappears from plain git diff.
  16. NadiaAnd appears in git diff staged. Which is the only command that shows exactly what your next commit will save. That's the habit: read it before every commit.
  17. ImranWhat about when one file has two changes, and I only want one in the commit?
  18. NadiaGit add dash p, patch mode. It walks through each hunk and asks: stage this hunk? You press y for yes, n for no. The lesson's example is a discount file with two changes.
  19. NadiaThe first hunk is the real fix: no member discount on orders below thirty dollars. The second is a debug line printing every call. You press y, then n. Only the fix goes in.
  20. ImranCan I do that in VS Code instead?
  21. NadiaYes. Open the file's diff, select the lines, and run stage selected ranges. But many developers like patch mode, because it makes you read every hunk. That's where stray debug lines and passwords get caught.
  22. ImranSpeaking of passwords. Gitignore.
  23. NadiaA file listing patterns of files Git should never offer to commit. Four groups belong in it. Dot env files with secrets. The node modules folder. Build output and logs. And editor and system files.
  24. ImranWhy is node modules so bad? It's just libraries.
  25. NadiaOften hundreds of megabytes, different on each operating system, and fully rebuilt by npm install from the package file and the lockfile. Commit the lockfile, never the folder.
  26. ImranAnd the dot env file. Say someone commits it by mistake, then adds it to gitignore the next day.
  27. NadiaThat's the trap. Gitignore only affects files Git isn't tracking yet. It never touches history. The password is still in the old commit, in every clone, and on GitHub if it was pushed.
  28. ImranSo the fix is?
  29. NadiaTreat the password as leaked and change it, first. Then stop tracking the file with git rm, dash dash cached, which keeps your local copy. And from day one, put dot env in gitignore, and commit a dot env example file with fake values.
  30. ImranNow history. How do I search it?
  31. NadiaGit log, with options. One line per commit. Filter by author. Filter by date, since and until. Only commits that touched one file. Or show the full change of each commit with dash p.
  32. ImranAnd for my finance question?
  33. NadiaThe pickaxe. Git log, dash capital S, followed by the text zero point zero seven. It lists only the commits that added or removed that text. Add dash p, and you see the change itself, with author, date and message.
  34. ImranIsn't git blame easier? It shows who changed each line.
  35. NadiaBlame shows only the last change to each line. If someone reformatted the file afterwards, blame points at them. The pickaxe looks through all of history, so it finds the real change.
  36. ImranNow the habits. What makes a good commit?
  37. NadiaIt does one thing, completely. Fix the eligibility check is one commit. Fix eligibility, rename variables, update the README and bump a library is four. A good test: if the subject needs the word and, it's probably two commits.
  38. ImranAnd the message format I keep seeing, feat colon, fix colon?
  39. NadiaConventional Commits. A type, a colon, and a short description. The specification defines feat for a new feature and fix for a bug fix. A widely used convention adds docs, refactor, test, chore and a few more.
  40. NadiaBecause it's machine readable, tools can build changelogs and version numbers from it. A fix means a patch release, a feat means a minor one, a breaking change a major one.
  41. ImranAnd the subject line style?
  42. NadiaImperative, as if finishing the sentence, this commit will. Fix: no member discount on orders below thirty dollars. About fifty characters. And when the why isn't obvious, a blank line and a body. Code shows what changed. Only the message can say why.
  43. NadiaThe second diagram shows the same week of work twice. If you're looking, red boxes on the left with messages like update, wip and final changes two. Green boxes on the right, each with a clear type and description.
  44. ImranAnd the question at the bottom: why no discount on small orders?
  45. NadiaOn the left, you read four diffs to find out. On the right, one line in the log answers it. And each commit on the right can be undone alone.
  46. NadiaThe Build-it section runs a realistic session. You start with a small discounts repo with three earlier commits from three people.
  47. NadiaYou make the eligibility fix and add a debug line in the discount file, and add a receipt function in another file. Then patch mode: yes to the fix, no to the debug line.
  48. ImranThen I compare the two diffs.
  49. NadiaThe staged one has only the fix. The unstaged one has the debug line and the receipt change. You commit the fix, throw away the debug line with git restore, and commit the receipt change separately. Two changes, two commits.
  50. NadiaThen you create the files a real project collects: a dot env, a node modules folder, build output and logs. Git status offers all of them. You write the gitignore, and they disappear. A check ignore command even tells you which rule ignored each file.
  51. ImranAnd the history part?
  52. NadiaYou filter the log by author and by date, print your own columns, and finally ask when the member rate became seven percent. The pickaxe finds one commit, raise member discount to seven percent for the festival, by Dev Two, on the second of October.
  53. ImranAnd I can check my commit messages without a reviewer?
  54. NadiaThat's the practice task. You write a small linter, a checker that reads a commit message and reports a missing type, a subject that's too long, a full stop at the end, or a body with no blank line before it. Your browser runs the tests.
  55. ImranWhat do I take to my team?
  56. NadiaAsk: can each commit in this pull request be described in one line, without the word and?
  57. ImranAnd: is dot env in the gitignore in every repo, and do we have an example file that lists every variable?
  58. NadiaGood. And one for audits: if I ask when this business rule changed, can someone answer from git log in a minute?
  59. NadiaOne to think about. You ran git add on a file. Now plain git diff prints nothing. Do you have no changes? Take a second.
  60. ImranI have changes. They're staged, so plain git diff has nothing left to show. Git diff staged would show them.
  61. NadiaExactly right. And git add never commits anything. It only copies the file into the staging area.
  62. ImranLet me sum up. One: git diff shows what I haven't staged, git diff staged shows what the commit will save, and I read the second before every commit.
  63. ImranTwo: git add dash p lets me commit one change at a time, and gitignore keeps secrets and node modules out.
  64. ImranThree: one change per commit, a Conventional Commit subject, and a body that says why.
  65. NadiaOne addition to the second point. Gitignore doesn't clean history. A password that was ever pushed is leaked, so change it first.
  66. NadiaNext time, branches. Trying a new shelf layout in one aisle without disturbing the rest of the shop.
  67. ImranSee you then.
Finished reading?Mark it complete to fill in its stop on your course line.

Use it at work

Runbook

Symptoms, first checks with commands, fixes and when to escalate, for one alert.

Open in the DevOps & TechOps Lab
Next stopBranches and merging
Was this lesson helpful?
Questions and discussion

Loading the discussion…