Git
Git is a popular open-source tool for code versioning.
- decentralized
- non-linear
- incremental change storage
Intro
Installation
- Linux: as a system package (
apt-get install git
,pacman -S git
, etc.) - Windows: there are several options
- basic version: https://git-scm.com/download/win
- Git for Windows: https://gitforwindows.org/
- as a system package in the Linux environment (WSL, MSYS2) – see Linux
There are also many GUIs and extensions/plugins for various IDEs. We will keep using the command line interface in a terminal – I think it is the best way to fully understand how git works and how it should be used.
Basic concepts
- repository
- commit
- branch
- working tree
First use – getting an existing project
git clone <url>
– creates a local copy of an existing repository- we will start with https://gitlab.fjfi.cvut.cz/klinkjak/turtle-graphics
All other commands must be run inside a project, i.e., in a directory
that contains .git/
(e.g. git clone
creates this).
git status
– shows the current status of a local repositorygit diff
– shows the difference between files in the working tree and the last commit (more exactlyHEAD
)git log
,git log -p
– shows the history of commitsgit log --graph
git log --oneline --graph --all --decorate
(you can create an alias, e.g.git config --global alias.ll "log --oneline --graph --all --decorate"
and then usegit ll
)
Workflow for programmers
Synchronization between local and remote repositories:
git fetch
– checks the current state of a remote repositorygit pull
– synchronizes the current branch from a remote to the local repository (this is actuallygit fetch
+git merge
/git rebase
, see later)git push
– synchronizes the current branch from the local repository to a remote
Working with branches:
git checkout
– switching the state of files in the working directory based on the given state in gitgit checkout <commit_id>
– switch to the given commit (arbitrary movement in history,HEAD
will become a reference to<commit_id>
)git checkout <branch>
– switching between branches,HEAD
will become a reference to<branch>
(alternative command:git switch <branch>
)git checkout <path/to/file>
– drop local changes in a file/directory, overwrite them with the currentHEAD
state (alternative command:git restore <path/to/file>
)
git checkout -b <branch>
– create a new branch from the currentHEAD
(alternative command:git switch --create <branch>
)
Metadata configuration (enough to do it just once; --global
applies the settings
for all projects, stored in the user‘s home directory; --local
changes the settings
only in the current project):
git config --global user.name "John Doe"
– set the author (programmer) namegit config --global user.email "john.doe@example.com"
– set the author (programmer) email
Adding changes:
git add
– add files to the stage area for the next commitgit add -p
– an interactive way of adding changes (you can add only some files or even just parts of some file for better logical separation of changes in commits)
git restore <file>
– drop changes in a file that are not committedgit reset <file>
orgit restore --staged <file>
– revert the action ofgit add
git mv <file_old> <file_new>
– rename a filegit rm <file>
– remove (delete) a filegit commit -m <message>
– create a new commit with the staged changesgit commit --amend
– commit the staged changes not to a new commit, but to the previous one (also useful for correcting the commit message)
NEVER use the commands git add .
and git commit -a
(git commit --all
)!!!
Always commit only the changes you really want to add to git. Furthermore, commits
should be divided systematically and reasonably „large“.
Hint: It is better to make multiple smaller commits than one big commit, because it is much easier to recombine the commits after the fact than splitting a commit into multiple commits.
.gitignore
The .gitignore
file allows to ignore specific files and directories that we never want to commit.
Such files are not shown in git status
and they will not be staged by the git add
command (unless we explicitly force it).
- comments start with
#
- each line contains the name of a file/directory that we want to ignore
- glob patterns with
*
are supported (e.g.*.backup
matches all files with the „.backup“ suffix)
In general, the following files/directories should be ignored:
- local configuration of editors/IDEs (e.g..
.vscode/
,.settings/
,.project/
) - backup files (
*.bak
,*.backup
,*.orig
) - binary files created by the compilation of source code (e.g.
*.exe
, on Linux it is not possible to match them all by a suffix)
Next concepts
- remote
- head, HEAD
- ref – named references, e.g. head, HEAD, remote, tag
- stage
- stash
And a lot more, see https://git-scm.com/docs/gitglossary
Merge and rebase
git merge <branch>
– merge given branch into the current branch (see visual reference)- commits from the participating branches are sorted chronologically in
git log
– it is not obvious from which branch they originate - there can be merge conflicts – the person doing the merge must resolve them manually, git records the resolution in a special commit at the end (so called „merge commit“)
- looking at the merge commit does not clearly show how the conflicts were resolved, and which commits caused them
- note that
git merge
does not linearize the history (each commit has still the same parent and children) – only the log sorts the commits by timestamp
- commits from the participating branches are sorted chronologically in
git rebase <branch>
– re-applies commits from the current branch such that the base of the branch is the given branch (see visual reference)- commits are logically reordered in the git database (the history is linearized)
- the person doing the rebase must resolve potential conflicts by each and every commit separately – more work for the programmer, but this practice leads to a clean and uncluttered history
- after a successful rebase, the branch can be merged without conflicts (and without creating the merge commit) – so called fast forward, the head of the target branch such as
refs/heads/main
is simply moved to the head of the other branch
git rebase --interactive
, overwriting history
Mergetool
git mergetool provides an interface between git and a text editor for resolving conflicts. Git subsequently opens the editor with files with a conflict. For each file it opens up a window with typically 4 versions:
- common version before branching (BASE)
- version from the target branch (LOCAL)
- version from the source branch (REMOTE)
- final version where conflicts must be resolved (MERGED)
The supported tools can be shown by running git mergetool --tool-help
.
Resources
- Version Control With Git
- A Visual Git Reference
- git add -p: The most powerful git feature you‘re not using yet
- How to write great commit messages
Rebase:
- A rebase-based workflow
- Why is rebase-then-merge better than just merge (complement)
- Introduction to Git rebase, force-push, and merge conflicts
- Git Branching - Rebasing
Workflow, best practices:
Cheat Sheet: