danilo.pianini@unibo.it
Compiled on: 2024-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-key
gpg --list-keys
gpg --keyserver hkp://pool.sks-keyservers.net --send-keys
Once 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:
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 end
destination
git 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
git
versions)
git pull --rebase=true
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 pop
git pull --rebase=true --autostash
Do 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 true
Select 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
reword
or r
edit
or e
squash
or s
fixup
or f
drop
or d
$ 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 --continue
Don’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 to
Cherry 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 start
git bisect bad
git bisect good
git bisect good
or git bisect bad
git bisect run
0
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-submodules
git mv path/to/submodule
git rm path/to/submodule
They 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 blame
Shows what revision and author last modified each line of a file
git blame foo
foo
, line by linegit blame -L 16,42 foo
foo
It 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 reflog
Reference 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-ish
es 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 commited and pushed
Events also dictate the file names:
applypatch-msg
commit-msg
fsmonitor-watchman
pre-applypatch
pre-commit
pre-merge-commit
pre-push
pre-rebase
pre-receive
prepare-commit-msg
post-update
push-to-checkout
update