You've been crunching day and night making sprite animation and finally you got it. Smooth and pleasant. With sight of relief you run your build... to find out that the character movement is broken!
You checked the movement code and... it is gone!
Before you go inventing a time machine stay awhile and listen. I will tell you how to do it.
Table of content
Luckily for us development is not always like running Diablo on hardcore without saves. You can and should save your progress during development, especially when you are working in team with other developers.
This is the point where version control system (VCS) becomes handy. Today we are going to talk about git.
Git is a distributed version control system that track file changes over time, allowing you to become time and space traveler.
Installing git
Installing git is very straight forward. Just visit the official web site downloads section https://git-scm.com/install/linux and follow the instructions for your operating system.
Once git installed let's check its version to verify installation
git --version
Ok, everything seems fine.
Init repository
Let's start new project
mkdir newgame
cd newgame
nvim main.py
if __name__ == "__main__":
print("This will be the next Diablo")
For git, to be able to track changes in your project you have to init repository first.
It can be done running init command
git init
This will create an empty repository inside your project folder. If you will list folder content you will find that .git folder was created
ls -la
So what, you will ask?
Repository trees
Yes, currently git did not track anything.
But let's run status command.
git status
As you can see the main.py file is listed in the "Untracked files" section. At least we track that we do not track anything so far :)
But what does it mean?
Now is the most crucial part to understand what git is and how it actually work.
Git is a file system with three states trees:
- Working tree
- Index
- HEAD
and in any moment in time git tracks your project state in all three trees.
Most command you will run will move your changes between this three states.
Let's start unrolling all of this from the "Working tree".
As it names suggests working tree is where work happens. You can edit, remove, move files and folders and git mostly does not care at all.
But when you are good with work you have done you can stage the changes into the "Index" tree using add command
git add main.py
and now if you will check the status
git status
you will see that main.py is moved from the "Untracked files" to the "Changes to be commited".
Let's add some code to the main.py file
from dataclasses import dataclass
@dataclass
class NPC():
name: str
greetings: str
if __name__ == "__main__":
decard = NPC(name="Deckard Cain", greetings="Stay awhile and listen")
print("You see an old man")
print(f"{decard.name}: {decard.greetings}")
Let's see what git thinks about this update
git status
Now you see another section: "Changes not staged to commit".
It means that now you have both staged version in the Index tree as well as new changes in Working tree.
You can always check what has been changed by running diff command
git diff
diff --git a/main.py b/main.py
index 5f7d3db..d9211a3 100644
--- a/main.py
+++ b/main.py
@@ -1,3 +1,13 @@
+from dataclasses import dataclass
+
+@dataclass
+class NPC():
+ name: str
+ greetings: str
+
if __name__ == "__main__":
- print("This will be the next Diablo")
+ decard = NPC(name="Deckard Cain", greetings="Stay awhile and listen")
+
+ print("You see an old man")
+ print(f"{decard.name}: {decard.greetings}")
As you can see git is watching you.
If you are good with the changes you can stage it once again
git add main.py
and checking the status you can see that there are no unstaged changes and also the diff is empty, meaning that all the changes are now in the Index tree.
git status
Commits
So far so good. But I've promised you a time machine.
This is the point where HEAD tree enters the scene.
You sure that you are done with greeting logic and want to make a save that you can load later if things will go wrong.
You can move your staged changed to the HEAD tree using commit command
git commit
If you run commit for the first time git will ask you to identify yourself, since every change should have it author
Git will ask you to write the commit message. Usually in the commit message you describe features or fixes you have made so later you can easily find your save point.
feat: Deckard Cain said hello!
Save the commit message, and there you have it, your first commit!
Let's take a quick look at the status
git status
it says nothing to commit, working tree clean which means that now we starting with clean Working and Index trees.
Let's go a bit crazy and delete the whole file!
rm -f main.py
ls -l
The code is gone... Diff renders all red...
diff --git a/main.py b/main.py
deleted file mode 100644
index d9211a3..0000000
--- a/main.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from dataclasses import dataclass
-
-@dataclass
-class NPC():
- name: str
- greetings: str
-
-if __name__ == "__main__":
- decard = NPC(name="Decard Cain", greetings="Stay awhile, and listen")
-
- print("You see an old man")
- print(f"{decard.name}: {decard.greetings}")
But now we have the time machine!
git reset --hard HEAD
ls -l
Deckard is back!
This particular command reset all trees to their states on the time of commit! But, we will get back to it later.
Commits history
Let's quickly make another commit
#...
akira = NPC("Akira", "Wanna some heal, adventurer?")
#...
Add changes to the commit
git add main.py
And commit them
git commit -m "feat: add Akira"
Now you have a new save point. But it doesn't mean that your previous commit is lost, you still can reset everything to the gold era of Deckard Cain!
You can look at yours commit history using log command
git log
commit 035e7ecf82bc3966332320c405b34645b5e7b591 (HEAD -> master)
Author: import sys <import.sys.dev@gmail.com>
Date: Wed Mar 4 21:26:03 2026 +0200
feat: add Akira
commit 6d5d2d1d8c1d7c67b08f85067724c515b3534ef0
Author: import sys <import.sys.dev@gmail.com>
Date: Wed Mar 4 20:51:02 2026 +0200
feat: Deckard Cain said hello!
You see both commits, and every commit has it unique ID.
The HEAD tree is now pointing to the latest commit.
Let's try to reset Akira changes.
Usually you do not need the whole commit information, but only the ID and the commit message, so you can run log with --oneline prefix
git log --oneline
035e7ec (HEAD -> master) feat: add Akira
6d5d2d1 feat: Deckard Cain said hello!
When executing reset command you can specify target commit id
git reset --soft 6d5d2d1
Soft reset means that we reset only HEAD tree, leaving changes staged at Index tree, so you can re-commit them if you want to. But this is the story for another time
Remotes
For now everything is happening on your local machine, and if you will accidentally delete .git folder you will lost all your commits and history. This is no good.
To address this issue remotes exists.
Remotes are basically servers where you can publish and store your commits. This can be literally any machine running git and accessible by the network. Or it can be platform like Github, GitLab, Bitbucket and so on.
We will talk about Github specifically, but the overall flow is identical to the every other platform.
Also I will not cover github account registration and setup, because I will never finish this tutorial otherwise :)
First of all let's check if we have any remotes configured for our repository
git remote -v
As expected there are none of them, so let's go to the github.com and create remote repository.
Now, as you have repository URL you can add remote to your local repository
git remote add origin git@github.com:import-sys/fantastic-fishstick.git
Origin is just a default name for the default remote.
Now if you check remotes
git remote -v
you will find that you have origin remote with two endpoints, for fetching and pushing commits.
origin git@github.com:import-sys/fantastic-fishstick.git (fetch)
origin git@github.com:import-sys/fantastic-fishstick.git (push)
Ok, now the fun part.
git push origin main
You just pushed all your commits to the remote, so you can access your work history even if you will burn your computer (don't do it). As a bonus you can share code with other developers to collaborate on the project. Hello FOSS.
Let's actually wipe everything, to prove the point
cd ..
rm -rf newgame
Your code is gone, you local git history is gone, but you can restore everything with a single command!
git clone git@github.com:import-sys/fantastic-fishstick.git newgame
ls -l newgame
You've just cloned remote repository into local repository.
Let's quickly check it
cd newgame && ls -l
git log --oneline
All the files and commits are there as if I have never delete them!
If someone make another commits to the remote repository you can pull remote changes using following command
git pull origin main
Now you are up to date with remote server, and in the log you will see the new commit
git log --oneline
That's it, your remote is all set up.
I know, it was tough. But understanding how you can manipulate git tree states is a key to more advanced git topics. Next time I will touch git topic we will discuss why trees are actually trees, cover branches, merging, rebasing and other cool stuff.










Top comments (0)