Ana has her repository and Bruno has his clone. From here on, both of them do exactly the same thing every day, dozens of times over: they edit files, decide which changes belong together and record them in the history. That three-step cycle — edit, stage, commit — is Git's heartbeat. Everything else in the course (branches, merges, rebase, remotes) is built on top of it.

In module 1 you learned the theory: the three areas (working tree, staging area and repository) and the three states (modified, staged, committed). This lesson sets that theory in motion. We are going to watch a file travel from one area to another, see which states it passes through from the moment Git knows nothing about it until it is committed, and learn how git status tells you at every moment exactly where you stand.

The goal is that, by the end, you can look at the output of git status in any repository in the world and know within a second what is going on and what the sensible next command is.

Contents

  1. The three-step cycle
  2. The three areas in motion
  3. The life cycle of a file
  4. git status: the compass
  5. git status --short: the short form and its table of codes
  6. Files you do not want to version: .gitignore in two minutes
  7. A complete working session of Ana's, narrated
  8. The cycle in daily practice

  1. The three-step cycle

Git's basic workflow has this shape:

graph LR
    E["1 · EDIT<br/>You change files<br/>in your folder"] --> P["2 · STAGE<br/>git add<br/>You choose what goes in"]
    P --> C["3 · COMMIT<br/>git commit<br/>It is recorded for good"]
    C -.->|round again| E

Three steps, two commands. What throws people at first is the middle step: why is "saving the changes" not enough? What is the staging area for?

The answer is that it separates two different decisions:

  • Editing answers "what have I changed?". That is work.
  • Staging answers "which of the things I changed form one meaningful unit?". That is judgement.

Imagine Ana spends the morning fixing a bug in app.js and, along the way, corrects a spelling mistake in README.md and tweaks a colour in styles.css. Those are three unrelated things. Without a staging area she would have two poor options: dump everything into one jumbled commit, or undo changes by hand so as to commit them separately. With the staging area she chooses: stage app.js alone, commit, stage README.md, commit, and so on.

The result is a history where each commit tells one single story. When somebody comes looking six months from now for why task deletion broke, they will find a commit that talks about exactly that and not about three tangled matters.

This idea — the atomic commit — matters so much that we develop it fully in the next lesson, Staging and Committing Changes.

  1. The three areas in motion

Let us bring back the diagram from Basic Git Terminology, this time with the commands that move information from one area to another:

graph LR
    WT["WORKING<br/>TREE<br/>Your files<br/>on disk"]
    IDX["STAGING<br/>AREA<br/>.git/index<br/>The draft of the<br/>next commit"]
    REPO["REPOSITORY<br/>.git/objects<br/>The permanent<br/>history"]

    WT -->|git add| IDX
    IDX -->|git commit| REPO
    IDX -->|git restore --staged| WT
    REPO -->|git checkout / git restore --source| WT

Three rules worth committing to memory:

  1. Only what is staged gets committed. git commit does not look at the working tree: it photographs the staging area. A change you have not staged does not go in, however safely it sits on disk.
  2. The staging area is a state, not a queue. It is not a list of pending commands; it is a complete version of the project. It holds the entire content of every file, not "the lines you added".
  3. The three areas can hold different content for the same file at the same time. This is beginners' confusion number one, and it is also the source of Git's power. We will see it in action in the narrated session.

  1. The life cycle of a file

Every file in your folder is, at any given moment, in one of four states. This diagram gathers them all and shows which command produces each transition:

stateDiagram-v2
    [*] --> Untracked: you create the file
    Untracked --> Staged: git add
    Staged --> Unmodified: git commit
    Unmodified --> Modified: you edit the file
    Modified --> Staged: git add
    Staged --> Modified: you edit it again
    Staged --> Untracked: git rm --cached
    Modified --> Unmodified: git restore
    Unmodified --> [*]: git rm

    Untracked: Untracked (new to Git)
    Unmodified: Unmodified (matches the last commit)
    Modified: Modified (changed, not staged)
    Staged: Staged (sitting in the index)

Let us take the four states one by one:

State Tracked? What it means How you see it in git status
Untracked No Git can see the file but has never recorded it. It is not part of the project In the Untracked files section
Unmodified Yes Identical to the last commit. There is nothing to do with it It does not appear: Git only reports what has changed
Modified Yes It has changed since the last commit, but has not been staged In Changes not staged for commit
Staged Yes Its current version will go into the next commit In Changes to be committed

There is an underlying distinction worth nailing down: tracked versus untracked. A file is tracked if it appears in the last commit or in the staging area — that is, if Git knows about it. The three states "unmodified", "modified" and "staged" are all variants of tracked. "Untracked" is the only category outside.

This distinction matters because many commands act only on tracked files. git commit -a, for instance, automatically stages modified files, but not untracked ones; those have to be added by hand at least once.

A key detail: a file can be in two states at once

Look at the Staged --> Modified transition in the diagram. It is real and it happens constantly:

  1. Ana modifies app.js and stages it with git add app.js. The staging area holds that version.
  2. Ana keeps working and touches app.js again. Now the disk holds a newer version than the staging area does.

The result: app.js shows up in both sections of git status, the staged one and the unstaged one. That is neither an error nor an anomaly: there are two different versions saved in two different areas, and if Ana committed now, the staged one (the older) would go in, not the one on disk.

  1. git status: the compass

If you had to keep just one Git command, this would be it. It changes nothing, it is instant, and it always answers the same four questions: which branch you are on, how you stand against the remote, what is staged and what is outstanding.

Let us read a full output, with every case present at once:

git status
On branch main
Your branch is up to date with 'origin/main'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	modified:   styles.css
	new file:   .gitignore

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   app.js
	deleted:    old.js

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	notes.txt

Let us dissect it block by block:

The header.

  • On branch main: the current branch. It is the first thing to check; being on the wrong branch is a classic.
  • Your branch is up to date with 'origin/main': a comparison against the remote-tracking branch. It only appears when a remote is configured, as in Bruno's clone. It can also say ahead by N commits or behind by N commits. We will develop this in module 4.

Changes to be committed — the staging area. Everything listed here will go into the next git commit. Each line carries a verb telling you the kind of change: modified, new file, deleted, renamed.

Changes not staged for commit — changes in the working tree to files Git already tracks, but which will not go into the next commit. Careful: only tracked files show up here.

Untracked files — files Git is seeing for the first time. They will never enter any commit until you run git add explicitly.

The hints in brackets. In every section Git tells you how to move forward and how to step back. They are worth reading: they are the best in-line documentation there is.

The outputs you will see most often

On branch main
nothing to commit, working tree clean

Everything committed, nothing outstanding. The three areas agree. This is the ideal state in which to switch to another task.

On branch main
Untracked files:
  (use "git add <file>..." to include in what will be committed)
	notes.txt

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

Nothing is staged. Run git commit now and Git would refuse, because there would be nothing to commit.

On branch main
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
	modified:   app.js

no changes added to commit (use "git add" and/or "git commit -a")

You have done work, but you have staged none of it. That final line is the warning: a bare git commit would achieve nothing.

  1. git status --short: the short form and its table of codes

The long output is excellent for learning, but it wears you down when you consult it forty times a day. The short form fits on one screen:

git status --short
git status -s          # equivalent
M  styles.css
A  .gitignore
 M app.js
 D old.js
?? notes.txt

The key is that there are two columns before the file name, and they mean different things:

  • Column 1 (left): the state in the STAGING AREA.
  • Column 2 (right): the state in the WORKING TREE.

Grasp that and a cryptic output turns into precise information. The possible codes:

Code Name Meaning
(space) unchanged Nothing to report in that area
M modified The content differs from the previous version
A added A new file, already staged
D deleted The file has been removed
R renamed The file has been moved or renamed
C copied The file was created as a copy of another
U updated but unmerged An unresolved conflict (you will meet it in module 3)
? untracked Git has never recorded it (shows up as ??)
! ignored Excluded by a rule (only with --ignored)

And now the combinations that actually turn up day to day:

Output Column 1 Column 2 Interpretation
M M Modified and staged. It goes in as it stands
M M Modified but not staged. It will not go in
MM M M Staged and modified again afterwards. The staged version goes in
A A A new file, already staged
AM A M Added, then modified after being added
D D Deleted, and the deletion is staged
D D Deleted from disk, but the deletion is not staged
R R Renamed, staged
?? Untracked
UU U U Conflict: both sides modified the file

Applied to the earlier example:

  • M styles.css → modified and staged. It will go in to the commit.
  • A .gitignore → new and staged. It will go in.
  • M app.js → modified but not staged. It will not go in. Note the leading space: it carries meaning.
  • D old.js → deleted from disk, not staged. The commit would still include the file.
  • ?? notes.txt → untracked. It will not go in.

A reading trick. Put a finger over the first column: what is left visible is "what is on disk". Lift the finger: what you see there is "what is going into the commit".

A very useful variant adds the branch information:

git status -sb
## main...origin/main [ahead 2]
M  styles.css
 M app.js
?? notes.txt

The first line sums up the current branch, its tracking branch and how far they have diverged. With -sb you get in five lines everything the long output takes twenty-five to say.

  1. Files you do not want to version: .gitignore in two minutes

There is a practical problem with Untracked files: most project folders accumulate files that must never be versioned. Installed dependencies, build output, editor temporary files, credentials, operating-system files… If they all show up in every git status, the signal drowns in noise and you end up ignoring the command's output, which is precisely what you do not want.

The solution is a file called .gitignore at the root of the project, with one pattern per line:

# Dependencies
node_modules/

# Credentials
.env

# Logs and temporary files
*.log
notes.txt

# Operating-system files
.DS_Store
Thumbs.db

Files matching those patterns vanish from git status and cannot be added by accident with git add .. The .gitignore itself is versioned: it is part of the project and must be the same for the whole team, which incidentally settles a long-running clash between the three operating systems of Ana, Bruno and Carla (.DS_Store on macOS, Thumbs.db on Windows).

Two nuances that head off the most common frustration:

  • .gitignore only affects untracked files. If a file is already tracked, adding it to .gitignore does not take it out of the project: it has to be removed from the index explicitly, something we will see in Staging and Committing Changes.
  • Write it early, ideally before the first git add, as we saw in Creating a Repository.

That gives you everything you need to work. Advanced patterns, negations with !, per-directory .gitignore files, the global one and .git/info/exclude are covered in depth in Ignoring Files with .gitignore.

  1. A complete working session of Ana's, narrated

Let us follow Ana through a whole afternoon. It is Tuesday and she wants to add a pending-task counter to task-manager. Starting point:

cd ~/projects/task-manager
git status
On branch main
nothing to commit, working tree clean

All clean: the three areas match the last commit. That is the right place to start something new from.

17:05 — She edits three files

She adds the counter element in index.html:

  <h1>Task Manager</h1>
  <p id="counter">0 tasks pending</p>
  <form id="new-task">

She styles it in styles.css:

#counter {
  color: #6b7280;
  font-size: 0.9rem;
  margin: 0 0 1rem;
}

And she adds the logic in app.js:

function updateCounter() {
  const pending = tasks.filter(function (t) { return !t.done; }).length;
  document.querySelector('#counter').textContent = pending + ' tasks pending';
}

Along the way she starts the project to try it out, which produces a log file, and she opens a notepad with loose ideas in it.

17:40 — First look at the status

git status
On branch main
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   app.js
	modified:   index.html
	modified:   styles.css

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	debug.log
	notes.txt

no changes added to commit (use "git add" and/or "git commit -a")

Five entries: three modified project files and two new files she does not want to version. That last line reminds her that, as things stand, git commit would do nothing.

17:42 — She strips out the noise

Before staging anything, she writes the .gitignore:

cat > .gitignore <<'END'
# Logs
*.log

# Personal notes
notes.txt
END
git status -s
 M app.js
 M index.html
 M styles.css
?? .gitignore

debug.log and notes.txt are gone. The output now carries nothing but signal. Look at the three lines beginning with a space: an empty column 1 means none of that would go into a commit yet.

17:45 — She stages

git add index.html styles.css app.js .gitignore
git status -s
A  .gitignore
M  app.js
M  index.html
M  styles.css

The Ms and the A have shifted to the first column. All four files are staged. In the long form:

git status
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	new file:   .gitignore
	modified:   app.js
	modified:   index.html
	modified:   styles.css

The "not staged" section has disappeared: nothing is left outstanding in the working tree.

17:52 — She spots a slip

Trying the page out, Ana sees the counter say "1 tasks pending". She fixes app.js so that the singular works:

function updateCounter() {
  const pending = tasks.filter(function (t) { return !t.done; }).length;
  const text = pending === 1 ? '1 task pending' : pending + ' tasks pending';
  document.querySelector('#counter').textContent = text;
}

And she checks again:

git status -s
A  .gitignore
MM app.js
M  index.html
M  styles.css

There it is, the interesting case: MM app.js. The file shows an M in both columns. It means exactly this:

  • Column 1 (M): there is a staged version of app.js — the 17:45 one, without the singular fix.
  • Column 2 (M): the file on disk differs from that staged version — it has the fix.

The long form makes it plainer still, because the same file appears twice:

git status
On branch main
Changes to be committed:
	new file:   .gitignore
	modified:   app.js
	modified:   index.html
	modified:   styles.css

Changes not staged for commit:
	modified:   app.js

If Ana committed now, the version with the slip in it would go in. This is Git's classic trap and the reason it pays to glance at git status right before committing.

17:53 — She stages again

git add app.js
git status -s
A  .gitignore
M  app.js
M  index.html
M  styles.css

The second column of app.js is blank again. Now the staged version is the good one.

17:55 — She commits

git commit -m "Add pending task counter"
[main 2f6a3c8] Add pending task counter
 4 files changed, 14 insertions(+), 1 deletion(-)
 create mode 100644 .gitignore
git status
On branch main
nothing to commit, working tree clean

Back to the starting point. The three areas agree once more, now over a new commit. And let us confirm that the ignored files are still there, merely invisible to Git:

ls
app.js  debug.log  index.html  notes.txt  README.md  styles.css
git status --ignored -s
!! debug.log
!! notes.txt

They are on disk, flagged as ignored. Git sees them and deliberately decides not to bother you with them.

The whole journey in one diagram

sequenceDiagram
    participant D as Working<br/>tree
    participant I as Staging<br/>area
    participant R as Repository

    Note over D,R: 17:05 — Ana edits 3 files
    Note over D: app.js, styles.css, index.html modified
    Note over D,R: 17:42 — She writes .gitignore
    Note over D: debug.log and notes.txt drop out of status
    D->>I: 17:45 git add (4 files)
    Note over D,R: 17:52 — She fixes a slip in app.js
    Note over D,I: app.js shows up as MM
    D->>I: 17:53 git add app.js
    I->>R: 17:55 git commit -m "Add pending task counter"
    Note over D,R: working tree clean

  1. The cycle in daily practice

Once the flow is second nature, day-to-day work boils down to a handful of habits:

The minimal loop, which you will run thousands of times:

git status          # where am I?
# ... edit ...
git add <files>     # what am I grouping together?
git status          # is that exactly what is going in?
git commit -m "..."  # record it

Four habits that make the difference:

  1. git status before you start. Knowing whether you are setting off from a clean tree or picking up something you left half-done yesterday stops two tasks ending up in one commit.
  2. git status before you commit. It is the only moment at which you can catch an MM or a forgotten file. It costs one second.
  3. Commit early and often. A commit is not a delivery or a release: it is a save point. A history of twenty small commits is infinitely more useful than one of two enormous ones.
  4. End the day with a clean tree whenever you can. If that is not possible, at least leave yourself a note about what you were in the middle of.

What to do when the loop breaks. These are the most frequent situations and the way out; all of them are covered in the next lesson:

Situation Command
I staged a file I did not mean to git restore --staged <file>
I want to discard my changes to a file git restore <file>
I got the last commit message wrong git commit --amend
I want to see exactly what I changed git diff (lesson 02-05)
I want to see what has been done so far git log (lesson 02-06)

Common Mistakes and Tips

  • Committing without looking at git status. The MM case — a file staged and then modified again — puts into the history a version that is not the one in front of you. A glance at the status before committing avoids it.
  • Thinking git add "saves" the file. git add saves nothing permanently: it copies the current content into the staging area. Keep editing and that copy goes stale.
  • Forgetting that git commit -a does not include untracked files. The -a option automatically stages tracked files that have been modified or deleted. A new file has to be added with git add at least once.
  • Ignoring the Untracked files section. That is where the new file you forgot to add hides — the one that stops the project building on your colleague's machine.
  • Misreading the two columns of --short. M (space then M) and M (M then space) mean opposite things. When in doubt, use the long form.
  • Working with a git status full of noise. If you have forty untracked files you are never going to version, write the .gitignore. A readable status is a tool; an unreadable one is an obstacle.
  • Adding .gitignore too late. If the file is already tracked, ignoring it has no effect. It has to come out of the index first.
  • Tip: define a short alias for the status, since you will use it constantly: git config --global alias.s "status -sb". After that, git s is enough. Aliases are covered in Git Log and Aliases.
  • Tip: if git status is slow on a large project, it is usually a symptom of too many untracked files (dependencies, build artefacts). A good .gitignore fixes it.

Exercises

Exercise 1: Read the status without running Git

A repository shows this output:

## main...origin/main [ahead 1]
A  config.json
MM app.js
 M styles.css
D  old.css
 D index.html
?? draft.txt
R  README.md -> GUIDE.md

Answer, with your reasoning:

  1. Which files will go into the next git commit (without using -a), and with what kind of change?
  2. What is going on with app.js? If it is committed now, which version ends up recorded?
  3. index.html has been deleted from disk. Will it disappear from the project when you commit?
  4. How many commits is this branch ahead of the remote?
  5. Write the sequence of commands that would leave the state ready for everything deleted, modified and renamed to go into the commit, except draft.txt, which must never be versioned.

Exercise 2: Reproduce the MM case

Create a test repository and deliberately provoke the situation in which an old version of a file gets committed:

  1. Create a repository with an app.js file containing the line const version = 1; and commit it.
  2. Change the line to const version = 2; and stage the change.
  3. Without committing, change the line to const version = 3;.
  4. Check the status in both the short and the long form.
  5. Commit without staging again.
  6. Work out with commands which version ended up recorded in the history and which one is still on your disk.
  7. Fix the situation so that the history reflects version 3.

Exercise 3: Clear the noise out of an inherited project

You are handed a project in which git status shows this:

On branch main
Untracked files:
	.env
	.vscode/settings.json
	build/index.js
	build/styles.css
	dist/package.zip
	report.log
	node_modules/  (thousands of files)
	src/newModule.js
	temp~

Only src/newModule.js should be versioned. Write:

  1. The .gitignore that leaves git status showing that file alone.
  2. The commands that verify it works, including an explicit check that .env is being ignored and by which rule.
  3. The sequence that commits the .gitignore and the new module in two separate commits, plus a justification for keeping them apart.

Solutions

Solution to Exercise 1

1. What will go into the commit. Everything with something other than a space in the first column:

File Code Goes in as
config.json A A new file
app.js MM Modified (the staged version)
old.css D Deleted
README.md -> GUIDE.md R Renamed

What does not go in is styles.css ( M), index.html ( D) and draft.txt (??), because their first column is empty or the file is unknown to Git.

2. app.js is the MM case: there is a staged version and, on top of it, the file on disk differs from that version. Committing will record the staged version, not the one on disk. The difference between the two will remain as an outstanding change after the commit.

3. index.html will not disappear. The code D says the deletion is on disk but not staged. The commit will still contain the file, so anyone cloning the repository will receive it. This is a common mistake: deleting through the file manager and forgetting to record the deletion.

4. One commit ahead, according to [ahead 1]: there is a local commit the remote does not have yet.

5. The sequence:

# 1. Exclude the draft so that it can never sneak in
echo "draft.txt" >> .gitignore

# 2. Stage everything outstanding on tracked files,
#    deletions included
git add -u

# 3. Stage the .gitignore, which is a new file
git add .gitignore

# 4. Verify before committing
git status -s
A  .gitignore
A  config.json
M  app.js
M  styles.css
D  old.css
D  index.html
R  README.md -> GUIDE.md

The second column is blank on every line and draft.txt has vanished: exactly what was asked for. The -u option of git add is the key here — it stages modifications and deletions of already tracked files without touching new ones — and we will study it in detail in the next lesson.

Solution to Exercise 2

Steps 1 to 3:

mkdir -p ~/practice/states && cd ~/practice/states
git init
echo "const version = 1;" > app.js
git add app.js
git commit -m "Add the initial version"

echo "const version = 2;" > app.js
git add app.js

echo "const version = 3;" > app.js

Step 4 — the status:

git status -s
# → MM app.js

git status
On branch main
Changes to be committed:
	modified:   app.js

Changes not staged for commit:
	modified:   app.js

The same file appears twice. The long form makes it obvious; the short one condenses it into MM.

Step 5 — commit:

git commit -m "Update the version"
[main 4b8e2f1] Update the version
 1 file changed, 1 insertion(+), 1 deletion(-)

Step 6 — what ended up where:

# What is in the history
git show HEAD:app.js
# → const version = 2;

# What is on your disk
cat app.js
# → const version = 3;

# And there is still work outstanding
git status -s
# →  M app.js

Confirmed: version 2 went in, the one that was staged. Version 3 is still in the working tree, unrecorded. This is exactly what ignoring an MM produces.

Step 7 — fixing it:

git add app.js
git commit -m "Fix the version to 3"
git show HEAD:app.js
# → const version = 3;

That is the safest solution: a new commit on top. (There is another option, git commit --amend, which corrects the previous commit instead of adding a new one; it is studied in the next lesson and should only be used on commits that have not been shared yet, because it rewrites the history.)

Solution to Exercise 3

1. The .gitignore:

# Dependencies
node_modules/

# Build artefacts
build/
dist/

# Local editor configuration
.vscode/

# Credentials
.env

# Logs and temporary files
*.log
*~

2. Verification:

git status -s
?? .gitignore
?? src/newModule.js

Exactly two entries: the .gitignore itself and the file we do want to version.

To find out why a particular file is ignored there is a dedicated command:

git check-ignore -v .env
.gitignore:11:.env	.env

The output gives the rules file, the line number, the pattern that matched and the file being evaluated. It is the right tool for debugging a .gitignore that "does not work": if the command returns nothing, no rule applies.

And one extra check that puts your mind at rest:

git status --ignored -s | head -5
!! .env
!! .vscode/
!! build/
!! dist/
!! report.log

3. Two separate commits:

git add .gitignore
git commit -m "Add .gitignore for deps, artefacts and secrets"

git add src/newModule.js
git commit -m "Add the report generation module"

Why keep them apart. They are two entirely unrelated changes: one configures what the project versions, the other adds functionality. Separating them brings concrete benefits:

  • Each commit can be described in a single honest sentence.
  • If someone has to check tomorrow why dist/ is ignored, they find a commit that talks about only that.
  • If the new module turns out to be a mistake and has to be undone, it is undone without dragging the .gitignore along with it.

This is the idea of the atomic commit, which we develop in the very next lesson.

Conclusion

You now have the complete cycle. To recap:

  • The basic flow is three steps — edit, stage, commit — and two commands, git add and git commit. The middle step exists to separate the work (what have I changed) from the judgement (what forms one meaningful unit).
  • The three areas are moved by specific commands: git add goes from the working tree to the staging area, git commit from the staging area to the repository, and git restore undoes in both directions.
  • A file passes through four states: untracked, unmodified, modified and staged. Only the last three correspond to tracked files, and many commands act on those alone.
  • One file can be in two states at once (the MM case), and if you fail to spot it you will commit a version that is not the one in front of you.
  • git status is the compass. In its long form it teaches; in its short form (-s, -sb) it informs at a glance, with two columns separating the staging area from the working tree.
  • .gitignore keeps the signal clean by excluding what should never be versioned, but it acts only on untracked files.

With this you can already work. What is missing is precision: so far we have staged whole files with git add <file>, and that is not always what you want. What if one file holds two changes that belong to different commits? How do you take something back out of the staging area? How do you delete or rename a file inside Git? And what if you get the message wrong on the commit you have just made?

All of that is the subject of the next lesson, Staging and Committing Changes: git add in depth with its -A, -u and . options — a classic source of confusion — and the interactive, hunk-by-hunk mode, how to unstage with git restore, git mv and git rm, and every variant of git commit, including --amend for correcting the last commit.

Mastering Git: From Beginner to Advanced

Module 1: Introduction to Git

Module 2: Basic Git Operations

Module 3: Branching and Merging

Module 4: Working with Remote Repositories

Module 5: Advanced Git Operations

Module 6: Git Tools and Techniques

Module 7: Collaboration and Workflow Strategies

Module 8: Git Best Practices and Tips

Module 9: Troubleshooting and Debugging

Module 10: Git in the Real World

© Copyright 2026. All rights reserved