danilo.pianini@unibo.itCompiled on: 2025-11-21 — printable version
Git supports two types of tags:
-a (unsigend), -s, or -u optionsgit describe) ignore lightweight tags by defaultNice people signs commits, certifying their authorship.
If you do not have a signature yet, time to create one
gpg --gen-keygpg --list-keysgpg --keyserver hkp://pool.sks-keyservers.net --send-keysOnce you have a private key to sign with, you can configure Git to use it for signing things by setting the user.signingkey config setting.
git config --global user.signingkey <YOUR_KEY_ID>
Classic situation:
$\Rightarrow$ You don’t want to have commits half-way, but you can’t lose what you have done…
git stash to the rescueStashing takes the dirty state of the working directory and saves it on a stack of unfinished changes that you can reapply at any time
git stash
git stash list
git stash apply [stash@{N}] [--index]
stash@{N}, applies the N-th stashed change--index is specified, the changes that were staged at the time of push get re-stagedgit stash drop stash@{N}
git stash pop
git stash apply && git stash drop stash@{0}git merge experiment
4 was developed after 3
Rebasing provides a way to alter the project history by changing the parent of (re-base) existing commits
git checkout experiment && git rebase master
git checkout experiment && git rebase master
git checkout master && git merge
git checkout master && git merge
fast-forward!
We want to leave server as is, but rebase client onto master
Option --onto can be used to transplant entire branches
git rebase --onto destination start end
start to enddestinationgit rebase --onto master server client
Reads: pick all commits from server (excluded) to client (included), remove them and reply them starting from master
Or: pick all commits from client, remove all those in server, then and reply them starting from master
pull is a non-atomic operation equivalent to git fetch && git merge FETCH_HEAD
There is no reason for the operation of reconciliation to be merge: it could well be rebase!
git fetch && git rebase FETCH_HEAD
Both operations are actually supported! The full commands would be:
git pull --rebase=false $\Rightarrow$ default behaviour (in old git versions)
git pull --rebase=true $\Rightarrow$ reunite using rebase!
Note: new versions of git require explicit configuration!
git config --global pull.rebase [true/false]Note: rebase is more sensitive to modified files in the worktree!
git stash && git pull --rebase=true && git stash popgit pull --rebase=true --autostashDo not rebase commits that exist outside your repository
Rebasing rewrites the project history and as such generates incompatible histories
--force rewrites history remotely and may delete other people’s commits!git pull --rebase is safe, if the local commits where never pushed in any remote
git config --global pull.rebase trueSelect depending on what you conceptually want:
They tell two stories:
Squashing is the practice of reassembling multiple commits into a single one
merge or manuallygit reset --soft HEAD~4
The worktree still has all changes from 6! What if we commit?
git commit
Commits 3 to 6 have been squashed into a single commit 3'!
Use a branch to “keep alive” the commit discarded by the reset
git branch target
now move master back to the last commit to preserve
now move master back to the last commit to preserve
git reset --hard HEAD~4
Ready: merge all changes in target, creating a single commit!
git merge --squash target && git commit
Remove the old branch: git branch -D target
Squashing results in further alteration than rebase
Merge when you want to retain history, keeping track of what happened
Rebase only when you are the only one with the commits, to favor linearity
Squash when some of the commits are somewhat “tests”, points in time you do not want to get back to anyway
-i) to rewrite historyrebase -i <tree-ish>
The most important commands are:
pick or p $\Rightarrow$ use commit, no changes
reword or r $\Rightarrow$ use commit, but edit the commit messageedit or e $\Rightarrow$ use commit, but stop for amendingsquash or s $\Rightarrow$ use commit, but meld into previous commitfixup or f $\Rightarrow$ like “squash”, but discard this commit’s log messagedrop or d $\Rightarrow$ remove commit$ git rebase -i HEAD~7
pick 41669e3 chore(deps): update shared-slides digest to 550f3a8
pick 053bfca chore(deps): update shared-slides digest to 249f7ae
pick 7af4843 chore(deps): update themes/reveal-hugo digest to 6c4bcb7
pick 3e4259e chore(deps): update shared-slides digest to 5384aaf
pick 117fa35 chore(deps): update themes/reveal-hugo digest to f9ead1f
pick 6eb514b chore(deps): update shared-slides digest to 0b573b0
pick 17743ec migrate from `GetJSON` to `resources.GetRemote` with `transform.Unmarshal`
Commits are listed listed in the opposite order than you normally see them using git log command.
reword 053bfca chore(deps): update shared-slides digest to 249f7ae
drop 41669e3 chore(deps): update shared-slides digest to 550f3a8
squash 7af4843 chore(deps): update themes/reveal-hugo digest to 6c4bcb7
squash 3e4259e chore(deps): update shared-slides digest to 5384aaf
squash 117fa35 chore(deps): update themes/reveal-hugo digest to f9ead1f
pick 6eb514b chore(deps): update shared-slides digest to 0b573b0
edit 17743ec migrate from `GetJSON` to `resources.GetRemote` with `transform.Unmarshal`
git rebase --continueDon’t push your work until you’re happy with it
One of the cardinal rules of Git is that, since so much work is local within your clone, you have a great deal of freedom to rewrite your history locally. However, once you push your work, it is a different story entirely, and you should consider pushed work as final unless you have good reason to change it. In short, you should avoid pushing your work until you’re happy with it and ready to share it with the rest of the world.
Selecting and importing a single commit (or commit range) from another branch
git cherry-pick <tree-ish>
<tree-ish> and adds it to HEAD<tree-ish> is a branch name, cherry picks the last commit of the branchgit cherry-pick from..to
from to ref toCherry picking is often useful for applying fixes or patches which are in development from other branches.
Bisection is a technique to find the commit that introduced a bug
git bisect startgit bisect badgit bisect goodgit bisect good or git bisect badgit bisect run0 if the bug is present, and with 1 to 127 except 125 otherwise
125 means “cannot be tested”git bisect run <command> <args>Using a repository within another repository
Typical use:
Adding an external submodule:
git submodule add <REPO_URL> <DESTINATION>
.gitmodules file in the repository root<DESTINATION>.gitmodules and <DESTINATION> must be tracked<DESTINATION>A plain clone does not initialize submodules. Special care applies.
git clone --recurse-submodules <URL> <DESTINATION>
If the repository has been cloned plainly, then submodules can be initialized manually
git submodule update --init --recursive
git submodule update --remote --recursive
Changes into submodules are dealt with as if they were on a separate repository
foreach can be used to run some command on all modules
git submodule foreach git pull--recurse-submodulesgit mv path/to/submodulegit rm path/to/submoduleThey allow, respectively, to move/remove an existing submodule
No single built-in command in old versions of git 😑 :
git submodule deinit -f -- sub/module/path
rm -rf .git/modules/sub/module/path
git rm -f sub/module/path
Note: Do not use a trailing slash on the submodule path when using these commands!
git blameShows what revision and author last modified each line of a file
git blame foo
foo, line by linegit blame -L 16,42 foo
fooIt is possible to use a regular expression as an argument to -L,
finding who did something specific
⬇️ git checkout HEAD~4 && git checkout -b very-important ⬇️
⬇️ Multiple extremely important commits ⬇️
⬇️ git checkout master ⬇️
git branch -D very-important ➡️
⬇️ git branch -D very-important ⬇️

git reflogReference logs: a diary of when branches and other references were updated
git reflog example:
c5c29c1 (HEAD -> master, origin/master, origin/HEAD) HEAD@{0}: checkout: moving from 2aa2cbd6f5cdb91a02f2061e029106878706bf49 to master
2aa2cbd HEAD@{1}: checkout: moving from master to HEAD~1
c5c29c1 (HEAD -> master, origin/master, origin/HEAD) HEAD@{2}: commit: add git blame
2aa2cbd HEAD@{3}: commit: improve git slides, prepare for blame and reflog
ae7eb17 HEAD@{4}: pull (finish): returning to refs/heads/master
ae7eb17 HEAD@{5}: pull (pick): improve the style
d2b71b0 HEAD@{6}: pull (start): checkout d2b71b04466d11498fdd10cc57da98dc0ab8eae2
e77f8a0 HEAD@{7}: commit: improve the style
5339051 (tag: 0.1.0-2022-09-08T174231) HEAD@{8}: commit: fix: forcibly set the targetPath of hugo
e5eeecd (tag: 0.1.0-2022-09-08T173405) HEAD@{9}: commit: ci: fix JamesIves/github-pages-deploy-action parameter names
50c5efe (tag: 0.1.0-2022-09-08T172922) HEAD@{10}: commit: fix: set up the custom css path
ba8fae0 (tag: 0.1.0-2022-09-08T162354) HEAD@{11}: commit: fix: make the new template work
8546a6c HEAD@{12}: commit: chore: switch to @cric96 fork of hugo-reveal
format: hash (labels) reference: operation (status): details
Any of the tree-ishes can be used to checkout!
git checkout HEAD@{6} would checkout commit d2b71b0!
git reflog![]()
Scripts that execute when some events happen, stored in .git/hooks
They are not part of the repository code, and hence they cannot get committed and pushed
Events also dictate the file names:
applypatch-msgcommit-msg $\Leftarrow$ particularly useful to enforce a commit message format!fsmonitor-watchmanpre-applypatchpre-commitpre-merge-commitpre-pushpre-rebasepre-receiveprepare-commit-msgpost-updatepush-to-checkoutupdate