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¶
Note
To summarize, git tracks changes in project files:
|
stage every new files |
|
stage tracked files and commit |
|
show status of all new and modified files |
|
show commit history |
|
unstage all |
|
undo commit ID and make new commit |
|
restore a file from last commit |
|
create a new branch |
|
delete a branch |
|
switch to an other branch |
|
merge changes from an other branch |
Basic usage¶
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