which git
Git’s locationgit --version
Git’s version
For Ubuntu, add this PPA to get the latest version:
$ sudo add-apt-repository ppa:git-core/ppa # apt update; apt install git
After first install, edit ~/.gitconfig
file:
[user]
name = Your Full Name
email = your@email.address
[core]
editor = vim
[color]
branch = auto
diff = auto
status = auto
[init]
defaultBranch = main
[diff]
colorMoved = default
[pull]
rebase = true
[alias]
a = add .
br = branch
ci = commit
eci = commit --allow-empty
c = commit -a --allow-empty-message --no-edit
co = checkout
conf = config --global --edit
df = diff
hdf = diff HEAD
sdf = diff --stat HEAD
cdf = diff --cached
wdf = diff --color-words
chdf = diff --color-words='[^[:space:]]|([[:alnum:]]|UTF_8_GUARD)+'
fixup = commit -a --fixup
fx = commit -a --fixup HEAD
la = log --patch
lg = log --color --graph --pretty=format:'%C(bold dim white)%h%Creset -%C(bold green)%d%Creset %s %C(bold green)(%ci) %C(bold blue)<%an>%Creset' --abbrev-commit
ll = log --oneline --graph
llfp = log --oneline --graph --first-parent
#lm = log --oneline --decorate=auto --author='Your Full Name'
p = push
pf = push --force
rb = rebase
rbic = rebase --interactive --rebase-merges --autosquash --committer-date-is-author-date
rs = restore
rt = reset
st = status
sw = switch
pick = cherry-pick
count-lines = "! git log --author=\"$1\" --pretty=tformat: --numstat | awk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf \"added lines: %s, removed lines: %s, total lines: %s\\n\", add, subs, loc }' #"
To change Git's language, add an alias to your ~/.bashrc
/ ~/.zshrc
file:
alias git="LANG=en_US.UTF-8 git"
alias {git,gti}="LANG=en_US.UTF-8 git" # If you often type "gti" instead of git...
To enable autocompletion on macOS, add this to ~/.zshrc
:
autoload -Uz compinit && compinit
Click to show git pull options
$ git pull
warning: Pulling without specifying how to reconcile divergent branches is
discouraged. You can squelch this message by running one of the following
commands sometime before your next pull:
git config pull.rebase false # merge (the default strategy)
git config pull.rebase true # rebase
git config pull.ff only # fast-forward only
You can replace "git config" with "git config --global" to set a default
preference for all repositories. You can also pass --rebase, --no-rebase,
or --ff-only on the command line to override the configured default per
invocation.
Click to show useful Vim settings
Edit ~/.vimrc
file.
set number
set mouse=a
set colorcolumn=73
set tabstop=8 softtabstop=0 expandtab shiftwidth=4 smarttab
highlight ColorColumn ctermbg=none
highlight ColorColumn ctermfg=Darkgrey
highlight ColorColumn cterm=underline
git init
Init new repogit commit --allow-empty -m "Initial commit"
Create an empty initial commit (blank repo)git commit --allow-empty-message --no-edit
Commit without a commit messagegit commit --fixup=<commit>
Commit and mark for fixupgit commit --squash=<commit>
Commit and mark for squashgit status
View current state of modificationsgit show [<object>]
Show information about a commit (or latest if no ID)git add .
Add all new files to the repository and stage them for commitgit add <file>
Add a specific file and stage it for commitgit add -N|--intent-to-add <file>
Add new file but without staging itgit add -u
Only add to files which are already staged, leaving others unstagedgit commit -m "Commit message"
Commit staged files onlygit commit -am "Commit message"
Commit all modifications (except untracked files)git commit --amend
Edit last commit message, and commit content if there are staged changesgit pull
Fetch and merge/rebase modifications from remotegit pull --all
Pull all local branchesgit fetch --all
Fetch changes froml all remotesgit push [<remote>] [<branch>]
Send your modifications (commits) to the remotegit push --all origin
Push everything to origin (including branches and tags)git push --force
Push local version to remote ignoring any conflicts (e.g. after a localreset
)git reset --soft HEAD~1
Undo last commit (soft, changes are kept)git reset --hard <commit>
Go back to repo state at given commit, newer commits will be lost. (Seepush --force
if the commits had been pushed already).git reset <commit> -- <file>
Reset single file to its version in a specific commit.git restore --staged <file>
Unstage file from staged-for-commit files listgit checkout <commit>
Explore the state of a given commitgit swich [-c] <branch>
Switch to a given branch.-c
Creates the branch if it doesn't existgit switch -c <branch> --track origin/<branch>
Create a local copy of a remote branchgit branch [<branch>] --set-upstream-to=origin/<branch>
Change remote tracking branchgit restore .
Restore all files to latest commit stategit stash
Put changes aside and go back to latest commit stategit stash pop
Restore stashed changes and removes stashgit stash apply
Restore stashed changes and keep stashgit stash --keep-index|-k
Stash only non git-added filesgit log|lg|l|ll|llg
Different types of logs, more or less verbosegit log --grep="<string>"
Search for a specific string in commit historygit log -L 100,150:path/to/file
Show log for lines 100 to 150 of filegit log -- path/to/file
Show log of specific filegit log --reverse
Show log in reverse ordergit log -G<search>
Search an expression in the loggit log --merges
Log all merge commitsgit log --no-merges
Log excluding merge commitsgit grep "<expression>"
Grep in files managed by Gitgit rm --cached file.txt
Stop tracking "file.txt" (effective after next commit)git rm -r --cached folder
Stop tracking "folder" (effective after next commit)git mv filename newname
Move or rename a file (maintaining versioning)git rebase master
Rebase current branch to mastergit rebase master feature
Rebasefeature
branch onto mastergit rebase [--interactive|-i] [--rebase-merges|-r] <HEAD~X|after-this-commit>
Interactive rebase. Without-r
option, merge commits will be lost. To work on the X last commits useHEAD~X
. To work on commits after a given commit, use a hash (the commit you give as parameter won't be affected, meaning it is the last commit you're OK with as is).git rebase --committer-date-is-author-date <commit or branch>
Rebase without changing the committer date (for rewords for instance). Works with--interactive
since Git 2.29 (Q4 2020)git reflog
Show logs of ref changes (branch switchings, rebasings, merges, resets, etc.)git cherry-pick <commit>
Merge a specific commit into current branch, but only the changes introduced by this particular commit (basically like applying the diff of a commit to the current branch).git cherry-pick --no-commit <commit>
Cherry pick without committing changesgit apply <patch file>
Applies a diff output to the current state. As an example,git diff > /home/patch.txt ; git reset . ; git apply /home/patch.txt
would save changes, remove changes, and then load them back.git blame -- path/to/file
Show which line corresponds to which commit and by whomgit blame -L 100,150 -- path/to/file
Show blame only for lines 100 to 150git blame -L 100,+50 -- path/to/file
Show blame only for 50 lines starting at 100git ls-files
List files managed by Gitgit remote prune origin
Remove local references to deleted remote branches (still appearing ingit branch -a
)git fetch --prune
Same asremote prune
git push --prune origin
Remove remote branches without a local counterpartgit config user.email "your@email.com"
Set email for current repository onlygit config --system --edit
Edit config globally for the entire machine
It's a good rule of thumb to merge a child branch into its parent to update a parent branch, and to rebase a child branch onto its parent to update a child branch. So, merge to update a parent branch, and rebase to update a child branch.
Never try to undo a merge commit using git revert
, only undo using git
reset
. git revert
only cancels the commit by creating a new commit
that is the exact opposite. But the merge commit stays unchanged in the
history, only now if you want to merge again (child into parent), the
diff will be completely off because the child is already merged.
The general-purpose git checkout
has been split into two commands :
git switch
to handle the branches partgit restore
to handle the files part
Most commands like add
, restore
, reset
, stash
, etc. can take a
--patch
or -p
parameter that prompts you, chunk of code by chunk of
code, whether you want to apply the command to it or not.
Use y
(yes), n
(no) or s
(split chunk) to apply you changes.
Interactive rebases are great but not very practical if you just want to squash a lot of commits into one.
To do this, you can just do a
git reset --soft <commit before those you want to squash>
, which will
remove the commits but keep the changes. Just re-commit on top of that.
If you don't want to look for commits to fixup and reorganize them by hand during a rebase, there's a way to do it automatically.
It works in two steps:
- Step 1: Add
--fixup=<commit to fixup>
when doing fixup commits (-m
is useless here) - Step 2: Add
--autosquash
to the interactive rebase
$ git commit -am "feat: Great new feature"
1026016 - feat: Great new feature
$ git commit -a --fixup=1026016
09e0e09 - fixup! feat: Great new feature
$ git commit -am "feat: Second great feature"
d0eb29b - feat: Second great feature
$ git commit -a --fixup=1026016
649e414 - fixup! feat: Great new feature
$ git rebase --interactive --autosquash HEAD~4
pick 1026016 feat: Great new feature
fixup 09e0e09 fixup! feat: Great new feature
fixup 649e414 fixup! feat: Great new feature
pick d0eb29b feat: Second great feature
Git keeps track of any action thats changes refs (switching branches, rebasing, merging, resetting, committing, etc.).
To access the logs for refs changes, use the command git reflog
:
$ git reflog
44c3ec8 HEAD@{0}: ...
25bf8d8 HEAD@{1}: ...
456e911 HEAD@{2}: ...
6ff6781 HEAD@{3}: ...
To undo an action, it works exactly like you'd undo a commit, by using
git reset
and the hash or HEAD@{x}
as reference:
$ git reset --hard 456e911 # Not necessarily a commit hash, can be a state hash
$ git reset --hard HEAD@{2} # Reset to the state of HEAD two moves ago
Git can help you find which commit introduced a specific bug. You give Git a known "bad" commit, and a knwow "good" commit, then Git will checkout commits by dichotomy. Each time you tell it if the checked ou commit is "good" or "bad", until you are left with the problematic one.
$ git bisect start
$ git bisect bad <bad commit>
$ git bisect good <good commit>
Or the same in one command:
$ git bisect start <bad commit> <good commit>
From here, Git checks out a commit approximatively in the middle between "bad" and "good".
$ git bisect bad # Tell Git this commit is bad
$ git bisect good # Tell Git this commit is good
Once done, Git will tell you the problematic commit:
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[23c5102268866666dc6695cc611e652281baf7fd] Commit introducing a bug
Then you can go back to normal:
$ git bisect reset
$ git grep <regexp> $(git rev-list --all)
Limited to subtree:
$ git grep <regexp> $(git rev-list --all -- lib/util) -- lib/util
In git blame
you see the last commit that edited a given line.
If you moved your codebase to a new code formatter, for instance, a lot of lines will show useless data.
In this case, you can use the --ignore-rev=
option, to ignore the
offending commit :
$ git blame -L714,718 modules/qwitus/contract.py
8b811a568b (Black 2023-07-18 11:06:52 +0200 714) cls._buttons.update(
8b811a568b (Black 2023-07-18 11:06:52 +0200 715) {
8b811a568b (Black 2023-07-18 11:06:52 +0200 716) "button_to_party": {"invisible": Eval("sub_type") == "20"},
8b811a568b (Black 2023-07-18 11:06:52 +0200 717) }
8b811a568b (Black 2023-07-18 11:06:52 +0200 718) )
$ git blame --ignore-rev=8b811a568b -L714,718 modules/qwitus/contract.py
27117beb26 modules/openup/contract.py (admreseau 2022-03-21 10:12:05 +0100 714) cls._buttons.update(
27117beb26 modules/openup/contract.py (admreseau 2022-03-21 10:12:05 +0100 715) {
27117beb26 modules/openup/contract.py (admreseau 2022-03-21 10:12:05 +0100 716) "button_to_party": {"invisible": Eval("sub_type") == "20"},
27117beb26 modules/openup/contract.py (admreseau 2022-03-21 10:12:05 +0100 717) }
27117beb26 modules/openup/contract.py (admreseau 2022-03-21 10:12:05 +0100 718) )
You could configure an alias if this is something you have to do on a regular basis.
If there are many commits to ignore, and they need to be updated
frequently, you can add them to a file and use the --ignore-revs-file=
option instead.
This file is usually called .git-blame-ignore-revs
, and can be added
once and for all to Git's local configuration :
git config --local blame.ignoreRevsFile .git-blame-ignore-revs
New .gitignore
rules don't apply to files already added to Git. You
have to manually git rm --cached
them.
There is a way to do that quicker for a lot of files:
$ # Make sure working directory is clean before doing that,
$ # commit your changes or reset --hard to HEAD.
$ git rm -r --cached . # Removed ALL files from Git
$ git add . # Add everything back (this will use new .gitignore rules)
$ git commit -m "chore: New .gitignore rules"
$ commit_date="2020-01-23 12:52:27 +0100"
$ export GIT_COMMITTER_DATE=$commit_date
$ export GIT_AUTHOR_DATE=$commit_date
$ git commit -am "Commit Message"
$ unset GIT_COMMITTER_DATE
$ unset GIT_AUTHOR_DATE
Make sure working directory is clean before doing that, commit your
changes or reset --hard
to HEAD
.
Start an interactive rebase (you can use your favorite rebase strategy):
$ git rebase --interactive <commit to split>~ # Notice the '~'
At the beginning of the line of the commit, pick
with edit
(or e
),
and save the file.
You're in rebase mode. Soft cancel the last commit, and unstage the changes:
$ git reset --soft HEAD~
$ git restore --staged .
Commit what you want in however many commits you need (git add -p
may
help). Then finish the rebase:
$ git rebase --continue
$ # For one command
$ GIT_EDITOR=gedit git rebase -i HEAD~4
$ # Or for multiple commands
$ export GIT_EDITOR=gedit
$ git commit -a
$ git rebase -i HEAD~4
There is a special .gitignore
-like file in the project's .git
folder
that won't be indexed (and so won't be part of the repository) but that
will still by applied to the local clone of the project.
You can find it in <project folder>/.git/info/exclude
. It works just
like a .gitignore
at the root of a project (it is project-wide).
Use this file when you're the only one on a team using certain tools or files.
If you have two (or more) separate projects that started out in different repositories, but now you want them in a single repository, you can.
Basically, you just add the second project as a remote to your current
project, then merge with the special flag --allow-unrelated-histories
(i.e., histories without a common ancestor).
$ git remote add otherproject
$ git fetch --all otherproject
$ git merge --allow-unrelated-histories otherproject/main
$ git remote remove otherproject
$ # Total only
$ git ls-files | xargs cat | wc -l
$ # Per file and total
$ git ls-files | xargs wc -l
git diff
View current unstaged modificationsgit diff --cached
View current staged modificationsgit diff somefile.txt
View modifications of a specific filegit diff branch1..branch2
Difference between branches at their tipgit diff branch1...branch2
Difference between branches at the tip of right branch and common ancestor commitgit diff master
Difference between current branch and mastergit diff --name-status
Only show file names (works with..
and...
syntax)git diff --color-words
Make a diff on the basis of words instead of linesgit diff --stat
Show diff stats (lines added, removed, etc.)git diff main..dev -- */templates/*.html
Diff only files matching a patterngit diff main..dev -- ':(exclude)*/templates/*.html'
Diff files excluding those matching a pattern
Sometimes you want to apply the state of a branch/commit to another branch, without doing a merge, rewriting or handling a broken history with potential conflicts to resolve.
This takes a pure data diff
between the two, not considering history,
and applies the changes to the current branch:
$ git switch <branch>
$ git diff <branch> <branchorcommittocopy> > /tmp/diff.txt
$ git apply /tmp/diff.txt
$ git commit -a
git branch
List existing branchesgit branch newbranch
Create new branch called "newbranch"git switch branch
Switch to "branch"git switch -c newbranch
Create "newbranch" and switch to itgit switch --orphan newbranch [&& git rm -rf .]
Create and switch to an orphan branch. Envenutally delete all the files and changes in the index (staging area) if you don't have an empty initial commit for example.git branch -d newbranch
Delete "newbranch"git branch -D newbranch
Force delete "newbranch" even if unmerged commitsgit push origin --delete newbranch
Delete remote branchgit merge newbranch
Merge "newbranch" into current branchgit merge <commit>
Merge a specific commit including all the changes leading up to this commitgit merge|pull --no-ff branch
Merge or pull a branch preventing fast-forwards (always create a merge commit)git merge|pull --ff-only branch
Merge or pull a branch imposing a fast-forward (never create a merge commit)git merge --squash <branch>
Squash all the commits before merging (only one commit merged)git merge -s ours <branch>
Merge and automatically resolve conflicts by keeping target branch (HEAD
) changesgit merge -s theirs <branch>
Merge and automatically resolve conflicts by keeping source branch changesgit merge-base <branch1> <branch2>
Get hash of last common ancestor commitgit branch -v
Show branches with their latest commitgit branch -r
List remote branches (branches the remote knows)
git tag <tagname> 29f478
Give commit n°29f478 the tag "tagname" (or HEAD if no commit given).git tag -a|--annotate <tagname> -m "Tag description"
Tag tip of branch with annotated taggit push --tags
Push tags to remove (not pushed by default)git tag -d|--delete <tagname>
Remove tag "tagname"git push --delete <tagname>
Remove tag from remotegit tag -l|--list "v1.8.5*"
Filter tagsgit tag -n99
Show full description (technically, 99 lines of text)git tag --sort=-version:refname
Sort tags in reverse (lexicographic) order
If you use annotated tags as Changelog entries, you can perform a simple extraction with most recent first like so:
$ git tag -n99 --sort=-v:refname
v0.2.1 Version 0.2.1
Fixes:
- Duis aute irure dolor in reprehenderit in voluptate.
- Velit esse cillum dolore eu fugiat nulla pariatur.
v0.2 Version 0.2
Ut enim ad minim veniam, quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat.
v0.1 Version 0.1
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do eiusmod tempor incididunt ut labore et dolore
magna aliqua.
To limit to v0.2
, you could add -l "v0.2*"
.
This is then very easy to parse for additional formatting as needed, or
just to pipe it to less
.
git clone https://yourgitserver/repo.git
Clone remote repositorygit clone --branch <branch> <remote>
Clone a specific branch of remotegit clone --depth <depth>
Shallow clone with history truncated to specified number of commitsgit remote -v
List remote repositoriesgit remote add origin https://yourgitserver/repo.git
Add a remote named "origin"git push -u origin yourbranch
Copy master branch to "origin" remotegit push -u origin yourbranch
Add local branch to "origin" remotegit remote set-url origin https://new.url.here
Change remote URLgit remote remove someremote
Remove remote repository (doesn’t delete it from the server, just unlinks it from local one)git remote rename someremote newname
Rename a remotegit push origin <commit>:yourbranch
Push up to a specific commit
Submodules enable you to make a large project containing one or more sub-projets. All sub-projects will have their own repo and can be worked on independently. The parent project will more or less just know the repo URL and the hash of the commit it currently needs.
This makes it easy to install the project on new machines automatically without error (just need to clone the parent project, and this will install all submodules/sub-repos automatically at the right commit or tag, everyone will share the same code).
Git sees the submodule as a file. When you change things inside the submodule (files changed, refs changed, etc.), the parent will show it as a unstaged file change in the working area. You can then commit it like any other file, and this will update the "default" state of the submodule for the project.
git submodule add https://github.com/<user>/<repo> <folder>
Adds submodule (works likegit clone
)git clone --recurse-submodules <project url>
Clone a repo with its submodulesgit submodule init
+git submodule update
Equivalent to--recurse-submodules
flag ongit clone
Once you've cloned the fork to a local repo, follow these steps to add the original repo as "upstream":
$ git remote add upstream https://originalprojectserver/repo.git # Step 1: Add original repo as upstream
$ git remote set-url --push upstream DISABLED # Step 2: Disable push on that repo. "DISABLED" is a fake URL which doesn't exist (doesn't need to be DISABLED)
$ git remote -v # Step 3: Check if added correctly
Maybe you also need to change the default remote for the main branch,
run git config --edit
.
With that done, you can update your fork (meaning pull the latest changes from the original repo) like so:
$ git switch <branch to update> # Step 1: Make sure you're on the right branch
$ git pull --ff-only upstream <branch to update> # Step 2: Pull changes from upstream (original repo). "--ff-only" means there won't be any merge commits. If it can't do it with a fast-forward it will fail.
$ git push [origin <branch to update>] # Step 3: Push updates to origin (forked repo)
You should never edit branches directly. Always work on a sub-branch. This way you'll always be able to pull changes from upstream without merge conflicts, and won't need to push back merge commits to solve the issue. It keeps everything clean.
If the upstream was forced pushed to, or its history has been rewritten, you need to hard update your fork like so (be careful though, all you changes to that branch will be lost. That's why you don't work on a branch directly) :
$ git switch <branch to update>
$ git fetch upstream
$ git reset --hard upstream/<branch to update>
$ git push --force origin <branch to update>
When you're done with work on a sub-branch, update your fork (see above) and rebase the sub-branch to the tip of the original branch before merging. If there are merge conflicts solve them in the sub-branch.
$ git switch <sub-branch>
$ git rebase <original branch>
Instead of rebasing, you can also merge the original branch into the sub-branch to updated the sub-branch.
$ git switch <sub-branch>
$ git merge --no-ff <original branch> # Will create a merge commit for reference
$ git fetch upstream # Get updates
$ git switch -c <same name as new upstream branch> # It doesn't matter from which branch you create this new one
$ git push -u <new branch> # Update origin (fork)
If you can use SSH key pairs, setup deploy keys from your repository. If
not (cheap shared hosting), you can almost do the same using deploy
tokens to authentify via HTTPS. This looks like
https://<token-name>:<token>@gitlab.com/
.
.git
folder location) above the Web
root in web projects, so that Git files can’t be accessed through HTTP
and remain private.
Also, production repos don’t need to store all the development history,
use the --depth 1
option to only download the origin/HEAD
state if
you want to save bandwidth and/or space .