DevOps.Academy
CoursesCurriculumPricing

    Loading…

    ↑ ↓ move · ↵ open · Ctrl K anywhere

    ◔My learningSign in
    DevOps.Academy

    Free, hands-on DevOps lessons. Made with care in India.

    Learn

    All coursesLinux roadmapCurriculumMy learning

    Academy

    PricingCertificates

    Legal

    PrivacyTermsContact

    devops.rajeev.pro

    Git & GitHubLesson 2 of 13
    Your progress · 0%
    On this page
    1. Three places a change lives
    2. The staging area
    3. Commits and messages
    4. .gitignore: what Git should never see
    5. Reading history with git log
    ↖ Course roadmap
    Lesson 2 · Repositories

    Commits: the working folder, the staging area and history

    How Git moves your changes from the working folder to the staging area to a commit, how to write commit messages people can use, keep secrets out with .gitignore, and read history with git log.

    30 min read · beginner · hands-on

    By the end of this lesson you'll be able to

    0 of 6 complete

    Three places a change lives

    Every change you make passes through three places. Once you can picture them, every Git command is just "move a change from here to there":

    Edit in the working folder, choose with git add, save with git commit.© Diagram: DevOps Academy

    Edit in the working folder, choose with git add, save with git commit.© Diagram: DevOps Academy
    PlaceWhat's thereMoved in by
    Working folderyour files as they are on disk, edits includedyou, editing
    Staging area (the index)the exact version of each file for the next commitgit add
    Repository (.git)every commit ever made, permanentgit commit

    git status tells you where everything is. Read it before every commit:

    $ git status
    On branch main
    
    No commits yet
    
    Untracked files:
      (use "git add <file>..." to include in what will be committed)
    	README.md
    	app.py
    
    nothing added to commit but untracked files present (use "git add" to track)
    
    git status
    • gitversion control
    • statuswhat changed

    Untracked files are ones Git has never been told about. They're never committed until you git add them.

    The staging area

    Edit, git add, git commit: watch the change move from your folder to staging to history.© Animation: DevOps Academy

    Edit, git add, git commit: watch the change move from your folder to staging to history.© Animation: DevOps Academy

    The staging area is why Git commits are clean: you choose exactly what goes into each commit, even when your working folder has five unrelated changes.

    $ git add README.md
    $ git status --short             # the compact form: two columns, staged | not staged
    A  README.md
    ?? app.py
    $ git commit -m "Add README"
    $ git add app.py
    $ git commit -m "Add the app entry point"
    
    git add README.md
    • gitversion control
    • addstage changes
    git status --short
    • statuswhat changed
    git commit -m "Add README"
    • commitsave staged changes
    --short codeMeans
    ??untracked
    A new file, staged
    M modified, staged
    Mmodified, not staged
    MMstaged, then changed again
    D deleted, staged

    Now see a change before and after staging it:

    $ echo "print('bye')" >> app.py
    $ git diff                        # working folder vs staging: not staged yet
    diff --git a/app.py b/app.py
    index 9f1b437..06e2325 100644
    --- a/app.py
    +++ b/app.py
    @@ -1 +1,2 @@
     print('hi')
    +print('bye')
    $ git add app.py
    $ git diff                        # nothing: everything is staged
    $ git diff --staged               # staging vs last commit: what you'll commit
    $ git restore --staged app.py     # unstage it (the edit stays in your file)
    $ git restore app.py              # throw the edit away (can't be undone)
    
    echo "print('bye')" >> app.py
    • echoprint text
    • >>append output to a file
    git diff
    • gitversion control
    git add app.py
    • addstage changes

    git add -p goes further: it shows each change in a file and asks y/n for each one, so one file with two unrelated fixes becomes two clean commits.

    The rounding fixes belong in the commit; the debug print doesn't. s splits this into two hunks, but the tax fix and the print touch, so they stay together: y stages the first, and e opens the second in your editor to delete the print lines first. ? explains every letter.© Screenshot: DevOps Academy

    The rounding fixes belong in the commit; the debug print doesn't. s splits this into two hunks, but the tax fix and the print touch, so they stay together: y stages the first, and e opens the second in your editor to delete the print lines first. ? explains every letter.© Screenshot: DevOps Academy
    Check before you commit

    Make git diff --staged a habit before every git commit. It's the last moment to spot a debug line, a password or a file you didn't mean to include, and it costs two seconds.

    Commits and messages

    A commit is a snapshot of the staged files, plus your name, the time, a message and a link to the commit before it. Each one has an ID: a 40-character hash such as 6e0c7057d4ddf7b9618bef0769e17e331429ebfa, usually shortened to the first seven (6e0c705).

    The message is for the person reading history a year from now, often you, trying to find why something changed:

    Fix crash when the cart is empty
    
    Checkout divided by the item count to show an average price, which
    failed with zero items. Show the total only when the cart is empty.
    
    Fixes #142
    
    RuleWhy
    Subject in the imperative, about 50 characters: "Fix …", "Add …", "Remove …"reads as "this commit will fix …", and fits in one-line views
    Blank line, then a body saying whythe diff already shows what changed
    One logical change per commiteasy to review, revert or cherry-pick
    Reference the issue: Fixes #142GitHub links it and closes the issue on merge

    Many teams use Conventional Commits (feat: add coupon codes, fix: empty cart crash), which tools read to generate changelogs and version numbers (the Tags and releases lesson). git commit without -m opens your editor for a message with a body.

    .gitignore: what Git should never see

    Some files must never be committed: secrets, dependencies you can reinstall, build output, editor clutter. List them in .gitignore at the top of the repository and commit that file:

    $ git status --short
    ?? .env
    ?? __pycache__/
    $ printf '.env\n__pycache__/\n*.log\n' > .gitignore
    $ git status --short             # .env and __pycache__ have disappeared
    ?? .gitignore
    $ git check-ignore -v .env       # which rule ignores it?
    .gitignore:1:.env	.env
    
    git status --short
    • gitversion control
    • statuswhat changed
    printf '.env\n__pycache__/\n*.log\n' > .gitignore
    • >write output to a file, replacing it
    # .gitignore: one pattern per line; comments go on their own line
    # one file, anywhere in the repository
    .env
    # every .log file
    *.log
    # a folder (the trailing slash means folders only)
    node_modules/
    # only build at the top level
    /build
    # an exception: don't ignore this one
    !keep.log
    

    A # only starts a comment at the beginning of a line: .env # secret would be read as one long file name. Start from GitHub's template for your language (linked below) instead of writing one from memory.

    Ignoring doesn't remove what's already committed

    .gitignore only affects untracked files. If .env was already committed, stop tracking it with git rm --cached .env and commit. The secret is still in history, so rotate it: change the password or key, because anyone with a clone can read old commits.

    Reading history with git log

    $ git log --oneline
    6e0c705 Add the app entry point
    fffd711 Add README
    $ git log --stat -1               # the last commit and the files it changed
    commit 6e0c7057d4ddf7b9618bef0769e17e331429ebfa
    Author: Rajeev Kumar <rajeev@example.com>
    Date:   Tue Oct 13 06:54:35 2026 +0000
    
        Add the app entry point
    
     app.py | 1 +
     1 file changed, 1 insertion(+)
    $ git log -p -1                   # the full patch of the last commit
    $ git show fffd711                # any one commit
    $ git log --oneline -- app.py     # only commits that touched app.py
    $ git log --oneline --graph --all # every branch, drawn as a graph
    
    git log --oneline
    • gitversion control
    • loghistory

    The History and diffs lesson goes much further: searching history, comparing branches and finding the commit that broke something.

    ⌘
    Mini mission

    Start a project repository the right way

    Create a repository for a small app that reads a secret from .env. Ignore the secret and generated files before the first commit, make three focused commits with good messages, and prove the secret was never committed.

    Input: Git installed and configured (lesson 1)Output: A repository with 3 commits and no .env in history
    Reveal one safe solution
    $ mkdir ~/projects/shop && cd ~/projects/shop && git init
    $ printf '.env\n__pycache__/\n*.log\n' > .gitignore
    $ git add .gitignore && git commit -m "Ignore secrets and generated files"
    $ echo "API_KEY=change-me" > .env
    $ echo "# Shop" > README.md && git add README.md && git commit -m "Add README"
    $ printf 'import os\nprint(os.environ.get("API_KEY", "no key"))\n' > app.py
    $ git add app.py && git diff --staged --stat && git commit -m "Add the app entry point"
     app.py | 2 ++
     1 file changed, 2 insertions(+)
    $ git log --oneline
    dad9c23 Add the app entry point
    2d89f66 Add README
    1221cd2 Ignore secrets and generated files
    $ git ls-files                    # everything Git tracks
    .gitignore
    README.md
    app.py
    $ git log --all --oneline -- .env # any commit that ever contained .env? (no output = none)
    
    mkdir ~/projects/shop && cd ~/projects/shop && git init
    • mkdirmake a directory
    • &&run the next command only if this one worked
    • cdchange directory
    • gitversion control
    • initcreate a repository here
    printf '.env\n__pycache__/\n*.log\n' > .gitignore
    • >write output to a file, replacing it
    git add .gitignore && git commit -m "Ignore secrets and generated files"
    • addstage changes
    • commitsave staged changes
    echo "API_KEY=change-me" > .env
    • echoprint text
    git log --oneline
    • loghistory

    Committing .gitignore first means no later git add . can sweep .env in. Your commit IDs will be different: they depend on your name, the time and the content.

    Try it yourself

    0 of 6 steps done

    Run each task in your own account or terminal, then tick it off.

    Knowledge check

    Quick check

    Question 1 of 5

    You changed app.py and ran git add app.py, then changed it again. What will git commit save?

    Go deeper

    Official reading for this lesson

    • GuidePro Git: recording changes to the repositoryPro Git ↗
    • GuidePro Git: viewing the commit historyPro Git ↗
    • Docsgitignore documentationGit ↗
    • Toolgithub/gitignore: templates for every languageGitHub ↗
    • ArticleConventional CommitsConventional Commits ↗

    Or get every quiz answer right and it completes itself.

    ← PreviousVersion control, and setting Git up onceNext lesson →Branches: work on many things without breaking main