Local repository

The ubiquitous version control tool is git.

Install git on your machine and run the configuration:

$ git config

Create a Local Repository

uv automatically initializes a local git repository when a project is initialized with uv init. Notice the .git and .gitignore in the project folder. The first is automatically handled with git commands, the second can be directly edited manually, more on that later.

To initialize a local repo in an existing project:

$ cd my-project
$ git init

Staging Area (Index)

To display the status of the files:

$ git status

Concept of staging using git add before committing: Git uses a two-step commit process. First, you stage changes using:

$ git add file_to_stage

This command places the changes into the staging area (also known as the index), preparing them to be included in the next commit. The staging area allows you to group changes meaningfully before committing, giving you full control over what becomes part of your project history.

To stage every file which has changed:

$ git add .

To avoid keeping track of some undesired files (private, temporary, etc), a .gitignore file can be added. The package management tool populates this file automatically, however one can add his own exclusions.

Commits

Recording changes with git commit. A commit saves a snapshot of all files currently staged. It includes a message, the author’s identity, a timestamp, and a unique hash identifier.

To create a commit:

$ git commit

This will open your configured text editor where you should write a clear and descriptive message.

Commit message guidelines:

  • Use the imperative mood (“Fix bug”, not “Fixed bug”)

  • First line should be concise (≤50 characters)

  • Optionally add a detailed explanation after a blank line

To give a special name to a commit (by default the last):

$ git commit TAG_NAME

To stage and commit in a single action all tracked files:

$ git commit -a

Branches

Creating and switching branches. Branches let you isolate changes from the main branch.

Create a new branch:

$ git branch new_feature

Switch to the branch:

$ git switch new_feature

Note

You must commit or stash your changes before switching branches, or Git will block the switch to prevent potential loss.

Use branches for developing features, experimenting, or fixing bugs independently from the main branch.

Merging and Conflict Resolution

Merging branches. Once a feature branch is complete (and committed), it can be merged back into the main branch.

$ git switch main
$ git merge new_feature

If both branches changed the same part of the same file, a merge conflict will occur. Git will mark the conflict using these markers in the affected files:

<<<<<<< HEAD
Current branch content
=======
Incoming branch content
>>>>>>> new_feature

After manually editing the file to resolve the conflict, you must stage and commit the resolved version.

After a merge, a branch can (and should) be deleted to keep the branch tree clean:

$ git branch -d new_feature

Log, Diff, and Commit Hashes

Viewing commit history. The Git log displays previous commits:

$ git log

This includes by default:

  • Commit hash (a unique SHA-1 identifier, e.g., e3f1e4d)

  • Author

  • Date

  • Commit message

The hash is critical for referencing commits in operations like revert, checkout, and reset.

Short and visual log:

$ git log --branches --graph --oneline --decorate=short

Viewing file changes

$ git diff               # Show unstaged changes
$ git diff --staged      # Show staged changes
$ git diff commit1 commit2   # Compare two commits

Undoing Changes

Git provides several tools to reverse or discard changes. The key concepts are:

  • unstaging: Remove files from the staging area but keep the working directory changes

  • restore: Restore a file from the last commit (discard working changes)

  • revert: Create a new commit that undoes an older commit

Unstage a file:

$ git reset

This removes the files from the index but does not change its content in your working directory.

Discard changes in the working directory:

$ git restore file_to_restore

This command overwrites file_to_restore with the version from the last commit, losing all local changes.

Undo a commit with a new commit (safe for shared history):

$ git revert ID

This creates a new commit that reverses the effects of the one specified by its hash ID.

Undoing with git

Undoing with git

Note

To summarize, git tracks changes in project files:

git add .

stage every new files

git commit -a

stage tracked files and commit

git status

show status of all new and modified files

git log --branches --graph --oneline --decorate=short

show commit history

git reset

unstage all

git revert ID

undo commit ID and make new commit

git restore FILE

restore a file from last commit

git branch NAME

create a new branch

git branch -d NAME

delete a branch

git switch NAME

switch to an other branch

git merge NAME

merge changes from an other branch

Basic usage

demo
  1# init
  2echo "############ init"
  3mkdir git_local_demo
  4cd git_local_demo
  5git init
  6echo "############ folder content:"
  7ls -a
  8echo "############ status:"
  9git -c color.ui=always status  
 10# stage and commit
 11echo "############ create an empty file, folder content:"
 12touch first_file
 13ls -a
 14echo "############ status:"
 15git -c color.ui=always status
 16echo "############ stage the file, status:"
 17git add first_file
 18git -c color.ui=always status
 19echo "############ commit:"
 20git commit -m "first commit"
 21echo "############ status:"
 22git -c color.ui=always status
 23echo "############ log:"
 24git log --branches --graph --oneline --decorate=short --color=always  
 25# change a file
 26echo "############ change file, file content:"
 27echo modif > first_file
 28cat first_file
 29echo "############ status:"
 30git -c color.ui=always status
 31echo "############ stage and commit:"
 32git commit -a -m "add word in first_file"
 33echo "############ log:"
 34git log --branches --graph --oneline  --decorate=short --color=always  
 35# ignore and tag
 36echo "############ add gitignore and a file to be ignored"
 37touch .gitignore
 38echo *.ignored > .gitignore
 39echo "############ gitignore content:"
 40cat .gitignore
 41touch file.ignored
 42echo "############ folder content:"
 43ls -a
 44git add .
 45echo "############ stage all, status:"
 46git -c color.ui=always status
 47echo "############ commit and tag"
 48git commit -m "add .gitignore and ignore .private"
 49git tag "my_first_tag"
 50echo "############ log:"
 51git log --branches --graph --oneline  --decorate=short --color=always  
 52# un-stage
 53echo "############ create second empty file, folder content:"
 54touch second_file
 55ls -a
 56echo "############ stage all, status:"
 57git add second_file
 58git -c color.ui=always status
 59echo "############ unstage, status:"
 60git reset
 61git -c color.ui=always status
 62echo "############ folder content:"
 63ls -a
 64# un-commit
 65echo "############ stage all, commit by mistake"
 66git add second_file
 67git commit -a -m "add second_file by mistake"
 68echo "############ log:"
 69git log --branches --graph --oneline  --decorate=short --color=always  
 70echo "############ folder content:"
 71ls -a
 72echo "############ uncommit"
 73git revert --no-edit HEAD
 74echo "############ log:"
 75git log --branches --graph --oneline  --decorate=short --color=always  
 76echo "############ folder content:"
 77ls -a
 78# restore to index
 79echo "error" >> first_file
 80echo "############ introduce error in file, file content:"
 81cat first_file
 82git restore first_file
 83echo "############ restore file, file content:"
 84cat first_file
 85# work with branch
 86echo "############ list branches:"
 87git branch
 88echo "############ create and switch to new branch"
 89git branch new_branch
 90git switch new_branch
 91git branch
 92echo "############ modif file in the new branch, file content:"
 93echo other_modif >> first_file
 94cat first_file
 95echo "############ stage and commit:"
 96git commit -a -m "add other modif"
 97echo "############ log:"
 98git log --branches --graph --oneline  --decorate=short --color=always  
 99echo "############ switch to master, file content:"
100git switch master
101cat first_file
102echo "############ merge in master:"
103git merge new_branch
104echo "############ file content:"
105cat first_file
106echo "############ delete merged branch"
107git branch -d new_branch
108echo "############ list branches:"
109git branch
110echo "############ modif file on the wrong branch, file content:"
111echo "yet_anoter_modif_not_to_be_done_on_this_branch" >> first_file
112cat first_file
113echo "############ status:"
114git -c color.ui=always status
115echo "############ create an other new branch and switch, list branches:"
116git branch other_new_branch
117git switch other_new_branch
118git branch
119echo "############ file content:"
120cat first_file
121echo "############ stage and commit:"
122git commit -a -m "add yet an other modif"
123echo "############ log:"
124git log --branches --graph --oneline  --decorate=short --color=always  
125echo "############ switch back to master, file content:"
126git switch master
127cat first_file
128echo "############ create a future merge conflict, file content:"
129echo "future_conflict">>first_file
130cat first_file
131echo "############ stage and commit:"
132git commit -a -m "add a future merge conflict"
133echo "############ log:"
134git log --branches --graph --oneline  --decorate=short --color=always  
135echo "############ merge in master:"
136git merge other_new_branch
137echo "############ file content:"
138cat first_file
139echo "############ resolve conflict (keeping both lines), file content:"
140echo "modif\nother_modif\nfuture_conflict\nyet_anoter_modif_not_to_be_done_on_this_branch" > first_file
141cat first_file
142echo "############ stage and commit:"
143git commit -a -m "merge and resolve"
144echo "############ log:"
145git log --branches --graph --oneline  --decorate=short --color=always  
146echo "############ delete merged branch"
147git branch -d other_new_branch
148echo "############ list branches:"
149git branch
150echo "############ log:"
151git log --branches --graph --oneline  --decorate=short --color=always  
152# diff
153echo "############ diff:"
154git diff my_first_tag HEAD --color=always
############ init
Initialized empty Git repository in /home/nbiboulet/Downloads/git_local_demo/.git/
############ folder content:
.
..
.git
############ status:
On branch master

No commits yet

nothing to commit (create/copy files and use "git add" to track)
############ create an empty file, folder content:
.
..
first_file
.git
############ status:
On branch master

No commits yet

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

nothing added to commit but untracked files present (use "git add" to track)
############ stage the file, status:
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
	new file:   first_file

############ commit:
[master (root-commit) f6bcabb] first commit
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 first_file
############ status:
On branch master
nothing to commit, working tree clean
############ log:
* f6bcabb (HEAD -> master) first commit
############ change file, file content:
modif
############ status:
On branch master
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:   first_file

no changes added to commit (use "git add" and/or "git commit -a")
############ stage and commit:
[master 78890c1] add word in first_file
 1 file changed, 1 insertion(+)
############ log:
* 78890c1 (HEAD -> master) add word in first_file
* f6bcabb first commit
############ add gitignore and a file to be ignored
############ gitignore content:
*.ignored
############ folder content:
.
..
file.ignored
first_file
.git
.gitignore
############ stage all, status:
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	new file:   .gitignore

############ commit and tag
[master 4d65dba] add .gitignore and ignore .private
 1 file changed, 1 insertion(+)
 create mode 100644 .gitignore
############ log:
* 4d65dba (HEAD -> master, tag: my_first_tag) add .gitignore and ignore .private
* 78890c1 add word in first_file
* f6bcabb first commit
############ create second empty file, folder content:
.
..
file.ignored
first_file
.git
.gitignore
second_file
############ stage all, status:
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	new file:   second_file

############ unstage, status:
On branch master
Untracked files:
  (use "git add <file>..." to include in what will be committed)
	second_file

nothing added to commit but untracked files present (use "git add" to track)
############ folder content:
.
..
file.ignored
first_file
.git
.gitignore
second_file
############ stage all, commit by mistake
[master 374de93] add second_file by mistake
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 second_file
############ log:
* 374de93 (HEAD -> master) add second_file by mistake
* 4d65dba (tag: my_first_tag) add .gitignore and ignore .private
* 78890c1 add word in first_file
* f6bcabb first commit
############ folder content:
.
..
file.ignored
first_file
.git
.gitignore
second_file
############ uncommit
[master b584098] Revert "add second_file by mistake"
 Date: Sun Jan 18 14:51:01 2026 +0100
 1 file changed, 0 insertions(+), 0 deletions(-)
 delete mode 100644 second_file
############ log:
* b584098 (HEAD -> master) Revert "add second_file by mistake"
* 374de93 add second_file by mistake
* 4d65dba (tag: my_first_tag) add .gitignore and ignore .private
* 78890c1 add word in first_file
* f6bcabb first commit
############ folder content:
.
..
file.ignored
first_file
.git
.gitignore
############ introduce error in file, file content:
modif
error
############ restore file, file content:
modif
############ list branches:
* master
############ create and switch to new branch
  master
* new_branch
############ modif file in the new branch, file content:
modif
other_modif
############ stage and commit:
[new_branch 5bfa027] add other modif
 1 file changed, 1 insertion(+)
############ log:
* 5bfa027 (HEAD -> new_branch) add other modif
* b584098 (master) Revert "add second_file by mistake"
* 374de93 add second_file by mistake
* 4d65dba (tag: my_first_tag) add .gitignore and ignore .private
* 78890c1 add word in first_file
* f6bcabb first commit
############ switch to master, file content:
modif
############ merge in master:
Updating b584098..5bfa027
Fast-forward
 first_file | 1 +
 1 file changed, 1 insertion(+)
############ file content:
modif
other_modif
############ delete merged branch
Deleted branch new_branch (was 5bfa027).
############ list branches:
* master
############ modif file on the wrong branch, file content:
modif
other_modif
yet_anoter_modif_not_to_be_done_on_this_branch
############ status:
On branch master
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:   first_file

no changes added to commit (use "git add" and/or "git commit -a")
############ create an other new branch and switch, list branches:
M	first_file
  master
* other_new_branch
############ file content:
modif
other_modif
yet_anoter_modif_not_to_be_done_on_this_branch
############ stage and commit:
[other_new_branch da7ce7c] add yet an other modif
 1 file changed, 1 insertion(+)
############ log:
* da7ce7c (HEAD -> other_new_branch) add yet an other modif
* 5bfa027 (master) add other modif
* b584098 Revert "add second_file by mistake"
* 374de93 add second_file by mistake
* 4d65dba (tag: my_first_tag) add .gitignore and ignore .private
* 78890c1 add word in first_file
* f6bcabb first commit
############ switch back to master, file content:
modif
other_modif
############ create a future merge conflict, file content:
modif
other_modif
future_conflict
############ stage and commit:
[master e7394e4] add a future merge conflict
 1 file changed, 1 insertion(+)
############ log:
* e7394e4 (HEAD -> master) add a future merge conflict
| * da7ce7c (other_new_branch) add yet an other modif
|/  
* 5bfa027 add other modif
* b584098 Revert "add second_file by mistake"
* 374de93 add second_file by mistake
* 4d65dba (tag: my_first_tag) add .gitignore and ignore .private
* 78890c1 add word in first_file
* f6bcabb first commit
############ merge in master:
Auto-merging first_file
CONFLICT (content): Merge conflict in first_file
Automatic merge failed; fix conflicts and then commit the result.
############ file content:
modif
other_modif
<<<<<<< HEAD
future_conflict
=======
yet_anoter_modif_not_to_be_done_on_this_branch
>>>>>>> other_new_branch
############ resolve conflict (keeping both lines), file content:
modif
other_modif
future_conflict
yet_anoter_modif_not_to_be_done_on_this_branch
############ stage and commit:
[master 234af9b] merge and resolve
############ log:
*   234af9b (HEAD -> master) merge and resolve
|\  
| * da7ce7c (other_new_branch) add yet an other modif
* | e7394e4 add a future merge conflict
|/  
* 5bfa027 add other modif
* b584098 Revert "add second_file by mistake"
* 374de93 add second_file by mistake
* 4d65dba (tag: my_first_tag) add .gitignore and ignore .private
* 78890c1 add word in first_file
* f6bcabb first commit
############ delete merged branch
Deleted branch other_new_branch (was da7ce7c).
############ list branches:
* master
############ log:
*   234af9b (HEAD -> master) merge and resolve
|\  
| * da7ce7c add yet an other modif
* | e7394e4 add a future merge conflict
|/  
* 5bfa027 add other modif
* b584098 Revert "add second_file by mistake"
* 374de93 add second_file by mistake
* 4d65dba (tag: my_first_tag) add .gitignore and ignore .private
* 78890c1 add word in first_file
* f6bcabb first commit
############ diff:
diff --git a/first_file b/first_file
index f9a2fdf..fdce95b 100644
--- a/first_file
+++ b/first_file
@@ -1 +1,4 @@
 modif
+other_modif
+future_conflict
+yet_anoter_modif_not_to_be_done_on_this_branch