AI Computer Institute
Expert-curated CS & AI curriculum aligned to CBSE standards. A bharath.ai initiative. About Us

Version Control with Git

📚 Projects & Applied⏱️ 22 min read🎓 Grade 8
✍️ AI Computer Institute Editorial Team Updated: August 2026 CBSE-aligned · Peer-reviewed · 22 min read
Content curated by subject matter experts with IIT/NIT backgrounds. All chapters are fact-checked against official CBSE/NCERT syllabi.

Suppose you are building a Python script for your school's AI club that calculates a cricket team's run rate during a match. You start with run_rate_v1.py. The next day you add a strike-rate calculator, so you save a copy as run_rate_v2.py, just in case the old one still works better for your practical exam. Two days later your friend emails you their edits, and now you have run_rate_v2_friend_edit.py. By the time your teacher asks for the final submission, your folder looks like this: run_rate_final.py, run_rate_FINAL2.py, run_rate_FINAL_USE_THIS_ONE.py. Nobody, including you, can say for certain which file has the working strike-rate function and which one is missing it. This is not a hypothetical problem — it is what happens to almost every student and every professional programmer the first time they write code without a system for tracking changes. Version control is the fix, and Git is the tool nearly every working programmer in the world uses to do it.

You have already used version control — you just didn't call it that

Open a Google Doc you have edited for a school project. Click File > Version history > See version history. You will see a timeline of snapshots — Tuesday 4:12 PM, Tuesday 6:45 PM, Wednesday 9:03 AM — and you can click any one of them to see exactly what the document looked like at that moment, or restore it. That timeline is version control in its purest form: instead of only keeping the current state of a file, the system keeps a sequence of past states too, each one labelled, each one recoverable.

Code has three problems that a Google Doc does not, and each one shapes how Git is designed. First, a real project is rarely one file — your run-rate calculator might eventually be five or six files (the calculator, a test file, a README), and a "version" really means a snapshot of all of them together at one instant, not one file's history in isolation. Second, when you look back at old versions of code, a timestamp like "Tuesday 6:45 PM" tells you nothing useful — you need a human sentence explaining why that change was made, such as "fixed strike rate formula, it was using overs instead of balls." Third, and most importantly, two or more people often need to work on the same code files at the same time without waiting in a queue for each other, and without silently erasing each other's work. Git was built to solve exactly these three problems. It was created in 2005 by Linus Torvalds — the same person who created the Linux operating system kernel — because the thousands of programmers around the world contributing to Linux needed a way to track changes across a huge number of files, written by many people, without a single central computer becoming a bottleneck or a single point of failure.

That last detail matters: Git is a distributed version control system. Older version-control tools kept the entire project history on one central server, and your computer only ever held whatever the current files looked like — if that server's disk failed, the history was gone. In Git, every single person who has copied ("cloned") the project has the entire history sitting on their own machine. This is also the direct answer to the single most common mix-up beginners make.

Misconception to correct immediately: Git and GitHub are not the same thing. Git is a program that runs on your own computer and tracks changes to your files — it works completely offline, on a laptop with no internet connection at all. GitHub is a website (owned by Microsoft) that hosts copies of Git repositories online so that people can share them, back them up, and collaborate over the internet. You can use Git your entire life and never touch GitHub. GitHub is simply the most popular place to park a Git repository so others can reach it — it is a convenience built on top of Git, not Git itself.

The three places every file lives in Git

Before any commands make sense, you need one mental model: in a Git project, a file you are tracking can sit in three different places, and moving it between them is a deliberate, two-step action you control.

  • The working directory is your desk — the actual files you see and edit in your code editor right now.
  • The staging area (Git also calls it "the index") is a tray on your desk. When you finish a change you're happy with, you place it in the tray to say "this specific change is ready to be part of the next snapshot."
  • The repository is a locked cupboard of permanent, labelled snapshots. Nothing enters the cupboard until you explicitly lock the tray's current contents inside it.

Two commands move a change between these three places: git add moves a change from your desk into the tray, and git commit locks whatever is currently in the tray into the cupboard as a new, permanent, labelled snapshot called a commit. Notice the two-step design is deliberate: you might edit three files but only want two of those changes in your next snapshot — Git lets you stage exactly what you want and leave the rest for later.

Working Directory (your desk) run_rate.py (edited) changes not yet tracked Staging Area (the tray) run_rate.py (staged) marked ready for next commit Repository (.git) (the cupboard) C1 C2 permanent, labelled snapshots git add git commit

A complete worked example, traced line by line

Let's build the run-rate calculator for real and watch exactly what Git reports at each step. All of this happens on your own computer, offline, inside a folder.

$ git init
Initialized empty Git repository in /home/student/cricket/.git/

git init creates a hidden .git folder — this is the actual "cupboard." Everything Git knows about your project lives inside that one hidden folder; delete it and the project reverts to being an ordinary, untracked folder of files.

Now create run_rate.py with this content:

runs = 145
overs = 20
run_rate = runs / overs
print(f"Run rate: {run_rate}")
$ git status
On branch main
No commits yet
Untracked files:
  (use "git add <file>..." to include in what will be committed)
    run_rate.py

Git has noticed a new file exists in the working directory, but since you have never told Git to track it, it calls this file "untracked" — it is on your desk, not even in the tray yet.

$ git add run_rate.py
$ git status
On branch main
No commits yet
Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
    new file:   run_rate.py

The file has moved into the tray. It is staged, but there is still no commit — if your computer crashed right now, the staged content would still be recoverable, but there would be no permanent, named snapshot yet.

$ git commit -m "Add script to calculate run rate"
[main (root-commit) 4f8a1c9] Add script to calculate run rate
 1 file changed, 4 insertions(+)
 create mode 100644 run_rate.py

This is the first commit. Git printed 4f8a1c9 — a short form of the commit's unique ID, a 40-character hexadecimal fingerprint (called a SHA-1 hash) computed from the exact file contents, the commit message, the timestamp, and the previous commit's ID. Two different commits, anywhere in the world, essentially never produce the same ID, because the hash is sensitive to every single byte of input — change even one character in the file, or one word in the message, and the entire ID changes completely. This is what lets Git compare two people's project histories and instantly know whether they match, without comparing every file byte by byte.

Now suppose you add a strike-rate function:

runs = 145
overs = 20
balls_faced = 132
run_rate = runs / overs
strike_rate = (runs / balls_faced) * 100
print(f"Run rate: {run_rate}")
print(f"Strike rate: {strike_rate:.2f}")

Before staging, check what actually changed:

$ git diff
diff --git a/run_rate.py b/run_rate.py
index 8c3f21a..b92e7d4 100644
--- a/run_rate.py
+++ b/run_rate.py
@@ -1,4 +1,7 @@
 runs = 145
 overs = 20
+balls_faced = 132
 run_rate = runs / overs
+strike_rate = (runs / balls_faced) * 100
 print(f"Run rate: {run_rate}")
+print(f"Strike rate: {strike_rate:.2f}")

Read a diff like this literally: a line starting with a plain space is unchanged context, shown so you can see where the change sits; a line starting with + was added; a line starting with - would mean a line was removed (there are none here, since we only added lines). The @@ -1,4 +1,7 @@ marker tells you the old version of this block started at line 1 and spanned 4 lines, while the new version starts at line 1 and spans 7 lines. This is exactly how a teacher tracking changes with a red pen would mark up your rough notebook — except precise, automatic, and permanent.

$ git add run_rate.py
$ git commit -m "Add strike rate calculation"
[main 9d2b6e0] Add strike rate calculation
 1 file changed, 3 insertions(+)

Now view the project's full history:

$ git log --oneline
9d2b6e0 Add strike rate calculation
4f8a1c9 Add script to calculate run rate

Newest commit first, each with its short hash and message. Nothing about the first commit was overwritten or lost when the second one was made — 4f8a1c9 still exists, permanently, exactly as it was, and you could return the entire working directory to that exact earlier state at any time with git checkout 4f8a1c9.

Misconception to correct: committing is not the same as saving a file. Pressing Ctrl+S in your editor overwrites the file on disk instantly, silently, with no record kept of what it used to say — if you save over a working function with a broken one, the working version is simply gone unless you remember to undo. You might press Ctrl+S fifteen times while writing one function. A commit, by contrast, is a deliberate checkpoint: you choose the moment, you stage exactly what should be included, and you write a sentence explaining why. Saving is continuous and forgetful; committing is occasional and permanent.

Branches: working on a risky idea without breaking what already works

Your run-rate calculator now works and is due tomorrow. You want to try adding a leaderboard feature ranking multiple teams, but you are not confident it will work in time, and you cannot afford to break the version that is due. This is precisely the situation branches exist for.

A branch is not a copy of your files — it is a lightweight, movable label pointing at a specific commit. When you "switch" to a branch, Git simply changes which commit's snapshot is currently placed in your working directory; nothing gets duplicated on disk. By default, every repository starts with one branch, usually named main. Creating a new branch just adds a second pointer next to the first one, both initially pointing at the same commit:

$ git branch leaderboard
$ git switch leaderboard
Switched to branch 'leaderboard'

From this point, any new commits you make attach to the leaderboard pointer, while main stays exactly where it was — untouched, still submittable, still safe. You edit the file, adding leaderboard code, and commit as usual:

$ git add run_rate.py
$ git commit -m "Add leaderboard ranking for multiple teams"
[leaderboard 7ac4f11] Add leaderboard ranking for multiple teams

If the experiment works, you bring it into main by switching back and merging:

$ git switch main
$ git merge leaderboard
Merge made by the 'ort' strategy.
 run_rate.py | 8 ++++++++
 1 file changed, 8 insertions(+)

If it doesn't work, you simply never merge it — you switch back to main, which was never touched, and delete the branch. Nothing about your working submission was ever at risk.

Merging is not always silent. If, meanwhile, someone had also edited the very same line on main in a conflicting way, Git cannot guess which version you want — it stops and marks a merge conflict directly inside the file, using markers like <<<<<<< HEAD and >>>>>>> leaderboard around the disputed lines, and waits for a human to decide which lines to keep before you commit the resolved result. Git will automatically combine changes only when it is confident no information will be silently lost; the moment two people touch the exact same line differently, it deliberately asks you rather than guessing.

main leaderboard branch C1 C2 branch point C3 C4 C5 merge commit

Read the diagram as a timeline moving left to right. The blue line is main: commits C1 and C2 happened, then nothing new was added to main directly for a while. The amber line is the leaderboard branch, forking off at C2 and gaining its own independent commits, C3 and C4, without touching C1 or C2 at all. When you ran git merge leaderboard from main, Git created C5 — a special merge commit that has two parents (C2's line and C4), combining both histories into one. Anyone reading the project's log later can see precisely when the branches diverged and precisely when they came back together.

Why this matters beyond one script

Real software is written by teams, not by one person typing alone, and that is true whether the team is three classmates finishing a CBSE AI/Python elective project, a robotics club preparing for a competition, or the thousands of volunteers who maintain the Linux kernel Git was originally built for. Without version control, teams either take turns editing one file (slow, and someone always forgets whose turn it is) or email zip files back and forth (which recreates the exact FINAL_v3_USE_THIS chaos from the opening of this chapter, multiplied by every teammate). Git lets every team member work on their own branch simultaneously, commit as often as they like with a clear message explaining each change, and merge their work together only when it is ready — with the entire history of who changed what, and why, permanently preserved and searchable through git log.

It is worth being precise about what Git does not do. Git tracks changes to files; it does not, by itself, share those files with anyone else — that requires a remote host such as GitHub, which is a separate tool built on top of Git (commands like git push and git pull bridge the two, but that is a topic for when you start collaborating online, not for today). And Git does not prevent mistakes — it only makes them recoverable. If you commit broken code, the commit still happened; what Git guarantees is that the working version from before your mistake is never gone, only one git checkout away.

Check your understanding

  1. You run git init, create quiz.py, then run git add quiz.py and git commit -m "first version". Immediately afterward, you edit quiz.py to fix a bug, but you do not run git add again. What will git status report about quiz.py, and why exactly that and not "staged" or "untracked"?
  2. A git diff shows the line -score = marks / 100 followed by +score = (marks / total_marks) * 100. In plain English, what single change was made to the code, and which symbol tells you the old line was removed rather than just commented differently?
  3. After three commits on main (C1, C2, C3), you run git checkout C1 to look at old code, make no changes, and simply close your laptop. Has C2 or C3 been deleted or altered in any way? Explain using the idea of what a commit actually is.
  4. Two teammates both branch off the same commit. One renames a function from calc() to calculate_score() on their branch. The other, on their own branch, edits the body of that same calc() function without renaming it. When these two branches are merged, will Git most likely merge automatically or report a conflict? Justify your answer using where in the file each change actually happened.

Answers

  1. git status will report quiz.py as modified, not staged for commit. It is not "untracked" because Git already has a committed snapshot of this file and recognises it as a tracked file being changed. It is not "staged" because a commit only ever snapshots exactly what was in the staging area at the moment of git commit — editing the file afterward changes the working directory copy, but that new edit has not been placed in the tray with git add yet, so it is invisible to the next commit until you stage it.
  2. The formula for computing score was changed from dividing by a fixed number, 100, to dividing by a variable, total_marks, and then multiplying by 100 to get a percentage. The leading - marks the exact line that existed before and no longer exists after the change; the leading + marks its exact replacement. A comment change would show the commented line itself changing, not a whole logical line being deleted and a different one appearing in its place.
  3. No. Commits are permanent, immutable snapshots stored in the repository the moment they are made; checking out an earlier commit only changes which snapshot is currently shown in your working directory — it does not touch the repository's stored history. C2 and C3 still exist exactly as they were and can be returned to at any time, for instance with git checkout main (assuming main still points at C3) or git checkout C3 directly.
  4. Git will most likely merge these automatically, without a conflict. Renaming a function's name happens on the line that declares it (for example, def calc(): becoming def calculate_score():), while editing the function's body changes different lines further down, inside the function. Since the two branches modified different lines of the file, Git's merge can combine both sets of changes without needing a human decision. A conflict would only occur if both teammates had edited the exact same line — for instance, if both had renamed the function to two different new names.

Think About It

Think about this: How would you explain version control with git to a friend who has never seen a computer? What real-world analogy would you use? Imagine you had to build a system using these concepts — what would be your first step? Try this: before moving on, write down three things you learned and one question you still have.

Practice Exercises

Now it is time to practice! Complete these challenges to solidify your understanding:

  • Exercise 1: Write a short program that demonstrates the core concept from this chapter. Test it with at least 3 different inputs.
  • Exercise 2: Find a real-world example where version control with git is used in an Indian company (like TCS, Infosys, Flipkart, or ISRO). Write a paragraph explaining the connection.
  • Exercise 3: Create a mind-map connecting version control with git to at least 3 other topics you have studied.

Key Takeaways — Summary and Recap

Let us recap what we covered: the core ideas behind version control with git, how they connect to real-world applications, and why they matter for your journey in computer science. Remember these key points as you move forward. For competitive exam preparation (CBSE, JEE, BITSAT), focus on understanding the WHY behind each concept, not just the WHAT.

← Testing and Debugging Python CodeCommand Line Mastery →

Found this useful? Share it!

📱 WhatsApp 🐦 Twitter 💼 LinkedIn