The Git Workflow — add, commit, push

8 min read

Every Git change passes through three stages — working directorystaging arearepository — and travels to your team via a fourth step: push. Master that four-beat rhythm (edit → add → commit → push) and you have the daily heartbeat of professional Git use. This lesson runs the entire flow end-to-end with real commands and real terminal output, then teaches the part beginners get wrong: writing commit messages that don't make your future self cry.

The three local stages

Git keeps three views of your project at once:

  • Working directory — your files as you see them in your editor. Anything you save here is visible immediately, but Git hasn't recorded it yet.
  • Staging area (also called the index) — a holding area for changes you want to include in the next commit. Think of it as a draft of your next save point. You can stage some changes and leave others for later.
  • Repository — the committed history. Permanent, named, recoverable snapshots.

You move changes between stages with git add (working → staging) and git commit (staging → repository). After a commit, git push uploads it to the remote (GitHub) so your team can see it.

Beat 1: edit a file

Working in the repo from the previous lesson:

cd webapp-tests
echo "describe('login', () => { it('works', () => {}) })" > tests/login.spec.js
git status
On branch main
Untracked files:
  (use "git add <file>..." to include in what will be committed)
        tests/login.spec.js

nothing added to commit but untracked files present (use "git add" to track)

The new file exists in the working directory. Git sees it but isn't tracking it yet.

Beat 2: stage with git add

Stage the file:

git add tests/login.spec.js
git status
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   tests/login.spec.js

The file moved from "untracked" to "changes to be committed." It's staged.

Two flavours of git add you'll use constantly:

git add tests/login.spec.js     # stage one specific file
git add tests/                  # stage everything in a folder
git add .                       # stage all changes in the current directory

git add . is convenient but blunt — it stages everything, including files you may not have meant to commit. Until you have a .gitignore set up (Chapter 5, Lesson 1), prefer naming files explicitly.

Why staging exists at all

Beginners ask: why two steps? Why not just commit directly? The answer is focused commits. Imagine you fixed a flaky test and updated a config file at the same time. Two unrelated changes in one commit is hard to review and hard to revert. Staging lets you commit them separately — git add tests/login.spec.js && git commit -m "fix flaky login test", then git add config.yml && git commit -m "bump CI timeout". Two clean commits. Two clean reverts. Two clean review conversations.

Beat 3: snapshot with git commit

A commit is a permanent, named snapshot of whatever's currently staged:

git commit -m "Add login test for invalid password scenario"
[main 4c48901] Add login test for invalid password scenario
 1 file changed, 1 insertion(+)
 create mode 100644 tests/login.spec.js

The string in quotes is the commit message — the most important part. Git also recorded the author (your user.name / user.email from Lesson 2), a timestamp, and a unique hash (4c48901) so this exact snapshot can be referenced forever.

Run git status again — clean:

On branch main
Your branch is ahead of 'origin/main' by 1 commit.

nothing to commit, working tree clean

And git log to see what you just made:

git log --oneline
4c48901 Add login test for invalid password scenario
8f31320 Update search index and glossary cross-links
dc57b17 Add Chapter 8 lessons: Capstone Project

Beat 4: ship with git push

Until now, the commit lives only on your laptop. To share it with your team:

git push
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 8 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 412 bytes | 412.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0), local objects: 0
To github.com:acme/webapp-tests.git
   8f31320..4c48901  main -> main

The new commit is now on GitHub. Teammates can git pull to receive it. CI/CD pipelines pick it up automatically and run your tests against it.

Writing commit messages that don't embarrass you

The single highest-leverage habit a junior QA engineer can build. Compare:

  • ❌ "update"
  • ❌ "fix"
  • ❌ "stuff"
  • ❌ "asdfgh"
  • ✅ "Add login test for invalid password scenario"
  • ✅ "Fix flaky checkout test by waiting for network idle"
  • ✅ "Update fixtures to cover the new tax-rate edge case"

The rules:

  1. Start with a verb in the imperative. "Add", "Fix", "Update", "Remove", "Refactor". Read it as "If applied, this commit will _____." It will Add login test. It will Fix flaky checkout test.
  2. Be specific. What did you change, and why is it visible at a glance? "Fix test" is not specific. "Fix flaky checkout test by waiting for network idle" is.
  3. Keep the first line under ~70 characters. Long messages can have a body — git commit (without -m) opens your editor for multi-line messages — but the first line is the headline.

A year from now you (or someone else) will run git blame on a broken test, see your commit, and either thank you or curse you. Write messages your future self will thank you for.

Useful inspection commands

While you work:

git diff                        # what's changed but not yet staged
git diff --staged               # what's staged but not yet committed
git log --oneline               # condensed history
git log -p tests/login.spec.js  # full history of changes to one file

git diff before staging is your "do these changes look right?" check. git diff --staged is your "is this what I'm about to commit?" check.

The full workflow

Step 1 of 4

Edit

Change files in your working directory. Save in your editor. Git sees the changes but doesn't track them yet.

A complete QA scenario

You've been asked to add three new tests for the search feature. Sequence:

cd webapp-tests
git status                                              # confirm clean start
# ...write the three test files in your editor...
git diff                                                # eyeball your changes
git add tests/search/                                   # stage just the search folder
git status                                              # confirm what's staged
git commit -m "Add search tests for empty, special-char, and pagination cases"
git push

Five commands. Three new tests on GitHub. CI fires automatically and runs them. Your team can now review, merge, and ship.

⚠️ Common mistakes

  • Committing without staging anything. git commit -m "..." with nothing staged prints "nothing to commit." Beginners panic and re-edit the file — the fix is git add first. (Or git commit -am "..." to add-and-commit modified tracked files in one step. It does NOT pick up brand-new files.)
  • Using git add . blindly. This stages every change in your current directory, including secrets, build outputs, and screenshots you didn't mean to ship. Until your .gitignore is solid, list files explicitly: git add tests/login.spec.js.
  • Forgetting to push. A common moment of horror: laptop dies, three days of commits gone with it because they only ever lived locally. Push at the end of every working session — even on a personal branch — so the remote always has a copy.

🎯 Practice task

A complete add → commit → push cycle. 20-25 minutes. Use the qa-sandbox repo from the previous lesson, or any practice repo of your own.

  1. In your qa-sandbox repo, create three files: README.md, tests/login.spec.js, and tests/search.spec.js. Put one line of placeholder content in each.
  2. Run git status. Confirm all three appear as untracked.
  3. Stage only the README: git add README.md. Run git status again — note that the README is staged but the two test files are still untracked. This is the fine-grained control staging gives you.
  4. Commit: git commit -m "Add README". Run git log --oneline to confirm the commit exists.
  5. Stage and commit the test files in a second commit: git add tests/ && git commit -m "Add login and search test scaffolds". Run git log --oneline — you now have two clean, focused commits.
  6. Stretch (push step): if you have a GitHub account, create an empty repo there, then locally run git remote add origin <url> and git push -u origin main. Refresh the GitHub page — your two commits are live. (The next chapter covers git remote and SSH keys in detail; this is just a preview.)

You now know the daily rhythm of working in Git. Chapter 2 introduces branches — the part that turns Git from "save points" into "parallel workstreams."

// tip to track lessons you complete and pick up where you left off across devices.