Git Version Control System
Distributed revision control system, computer program, designed to help people create other computer program together.
Git is a free and open source distributed version control system designed to handle everything from small to very large projects with speed and efficiency. — Git
Git is useful to work with code without fear, you can experiment with code, use multiple branches as features tree, if your decision was incorrect, you can easily revert you changes. It’s also useful for reviewing your changes and collaborate.
Additional details can be found in @ProGitChacon2022 book. You
can also use built-in help: man git
, git help
or git [command] –help
.
After installing git, you usually need to configure it, here minimal configuration example, with my personal data:
Main objects in Git is commits and branches, they can be presented as nodes graph:
Git branching and committing
Does Git store an entire snapshot of files per commit?
Yes! Git stores an entire snapshot of files (not file changes) on a per-commit
level, but with some optimizations (data deduplication with pointers and
compression). So by default Git not good solution to work with large binary
files.
Git tips
Here I store various flashcards, how to use git (sort of cheat sheet).
General
Check the Git version:
git --version
Show general help:
git --help
Show help on a Git subcommand (like clone
, add
, push
, log
, etc.):
git help subcommand
Execute a some Git subcommand:
git subcommand
Execute a Git subcommand on a custom repository root path:
git -C path/to/repo subcommand
Execute a Git subcommand with a given configuration set:
git -c 'config.key=value' subcommand
Create repository
Create a new local repository, project_name is optional. If no project name specified you’ll create git repository in current directory. You can also use non-existing nested directories, git automatically create needed structure.
git init [project_name]
Download from an existing repository. URL can be http[s]...
or git@...
(require specific private ssh key). If project path specified, git will clone into this directory.
git clone URL [project_path]
Perform a shallow clone, to only get the latest commits, which helps to save data (good for limited data connections) when cloning large repos.
git clone --depth 1 <remote-url>
Unshallow a clone.
git pull --unshallow
Git aliases
Alternative commands, like git s
and g
are special aliases, require specific git alias configuration.
Check your aliases:
bat ~/.gitconfig
Observe a Repository
List new or modified files not yet committed (changes from last commit)
git status
or git s
Show the changes to files not yet staged (committed before).
Pretty useful to see what you changed before stage it.
git diff
Show the changes to staged files (not committed)
git diff --cached
Show all staged and unstaged file changes
git diff HEAD
Show the changes between two commit ID’s
git diff commit_id_1 commit_id_2
Can be used with HEAD
as well.
Show changes to files WITHOUT considering them a part of git. This can be used to diff files which are not part of a git repo!
git diff --no-index path/to/file/A path/to/file/B
List the change dates and authors (who committed) for a file
git blame [file]
Show the file changes (not a diff) for a commit ID and/or file
git show [commit]:[file]
Show full change history. Is possible to list remote changes?
git log
, gl
is mine custom alias for graph view of commits.
Yes, you can log the commits of a remote repo as well (remote/branch):
git log origin/main
How customize output of git log
, for example:
- show full branch names
- hide branch names
- compact view of log, limit to 1 commit
- graph mode and all branches
A: decorate flags git log --decorate=full
, show full branch namesgit log --decorate=no
, hide branch names B: compact view of log, limit to 1 commitgit log --oneline -n1
C: graph mode and all branchesgit log --oneline --graph --all
git log --oneline --decorate --graph --parents
$ git log --oneline --decorate --graph --parents
* cb0280e a563a6a a3c16d4 (HEAD -> main) F: Merge branch 'add_classics', amend
|\
| * a3c16d4 a7bb5fe (add_classics) D:\ add classic
* | a563a6a a7bb5fe E: update contents
|/
* a7bb5fe ea61d81 a772d04 Merge branch 'remake'
|\
| * a772d04 a9333c8 (remake) Add remake
| * a9333c8 1656f66 (develop) ad second commit
| * 1656f66 1f7ea8a Test branch
* | ea61d81 1f7ea8a add
|/
* 1f7ea8a d05a05c C: add EN quotes
* d05a05c 351cadb B: add titles
* 351cadb A: add contents.md
Can you explain output (only significant parts)?
*
asterisk represent commitcb0280e a563a6a a3c16d4 (HEAD -> main)
, first hash it’s merg-commit hash, second two are parent commits (merge from → to).- the next section is a visual representation of the branch structure. It shows the commits on the add_classics branch and the main branch before the merge. Notice that they both share a common parent.
- etc.
You have this git graph, which type of merge Git will do?
Git will do a fast-forward merge, which means that it will simply move the HEAD
pointer from B to E, and we don’t need to create a new merge commit.
Get details of repository object (by commit hash)
git cat-file -p [commit hash]
What .git/refs/heads/main
this file contains?
It’s a file which contain the main
branch ref (commit hash that the branch
points to).
Git log, but limit to the last 10 commits and disable pager
git --no-pager log -n 10
Find git commit that introduced a string in any branch (search substring in any
branch/history).
git log -S <string> --source --all
View commits which would be pushed (commits in current local branch).
git log @{u}..
View changes which are new on a feature branch.
Show change history for file/directory including diffs
git log -p [file/directory]
View differences of branches/stash with meld, other tool?
TODO: review this
YOU CAN EDIT files in meld and save them!
Branches
Working With Branches List all local branches
git branch
List all branches, local and remote
git branch -a[v]
Create a new branch called new branch, how to create and switch in same
time?
git branch new_branch
git switch -c new_branch
Delete the branch called my branch
git branch -d my_branch
Force Delete the branch “branch_name”.
git branch -D [branch_name]
To delete a remote branch “branch_name”:
git push --delete origin [branch_name]
or
git push origin :<remotebranch>
How to change remote branch (origin
) URL?
git config remote.origin.url <new_url>
How to rename (move) a branch?
git branch -m[--move] old_name new_name
Continue merge after resolving conflicts
git merge --continue
And if you don’t want to edit the message when continuing/resuming the merge:
git merge --continue --no-edit
Branches: To delete all branches on remote that are already merged:
git branch --merged | egrep -v "(^*|main|dev)" | xargs git branch -d
Switch to a my_branch
git switch my_branch
Merge branch foo into branch bar. First we need to find/switch to merge
base (the best common ancestor?) of two branches, then we can merge them.
git switch bar; git merge foo
Create a bare branch; without any commits.
git checkout --orphan branch_name
Checkout a new branch from a different starting point.
git checkout -b main upstream/main
Reset local branch to origin branch, then checkout it.
git checkout -B main origin/main
. If -B is given,
Undo parts of the last commit in a specific file.
git checkout -p HEAD^ -- /path/to/file
Revert a commit, but keep the history of the event as a separate commit.
git revert <commit SHA>
Perform an interactive rebase for the last (prior) 7 commits:
git rebase -i @~7
Tag the current commit
git tag my_tag
git tag -a <tag> <commit> -m "<commit message>"
Push a tag to remote:
git push origin <tagname>
To delete a tag locally
git tag -d <tagname>
Sign new tags:
git tag -s v1.5 -m 'my signed 1.5 tag'
Delete a tag <tagname>
on remote
git push --delete origin <tagname>
Make an existing branch track a remote branch.
git branch -u upstream/foo
Changes into repository
Stages the file, ready for commit
git add [file]
Stage all changed files or files which ready for commit
git add .
git add [--all | -A]
Stop tracking the file completely (leave it on disk) and remove from repo!
git rm --cached [file]
Here exist alternative "un-track"
and "track"
commands, with this solution
file will stay in repo, but will not be tracked (updated) anymore, e.g., a
dev.properties file that you would need to change for your local environment but
you would never want to check in these changes:
git update-index --assume-unchanged <file>
If you want to start tracking changes again:
git update-index --no-assume-unchanged <file>
To list untracked files.
git ls-files --others --exclude-standard
Remove untracked files.
Use it with -n
to see what will be removed.
`git clean -f -d“
Remove untracked and ignored files.
Use it with -n
to see what will be removed.
git clean -f -d -x
Commit all staged files (staged changes) to versioned history
git commit -m "Your commit message"
7 digits are the Git default for a short SHA, so that’s fine for most projects.
Edit previous commit message.
git commit --amend
Change last commit message (one command).
git commit --amend -m "New commit message"
Change author of a commit.
git commit --amend --author="Author Name <[email protected]>"
Commit in the past
Newer versions of Git allow --date="2 day ago"
usage for commit flag.
git commit --date "2 day ago" -m "Your commit message"
Commit all your tracked files to versioned history
git commit -am "commit message
Sign a commit
git commit -a -S -m 'Signed commit'
Unstaged file, keeping the file changes
git reset [file]
Undo last commit but keep the changes
git reset --soft HEAD~1
Revert everything to the last commit. So it removes staged and working directory
changes.
git reset --hard
but be careful, you can lose your changes. Maybe need stash
them first.
Undo last commit. If you want to nuke commit C to never see it again:
A-B-[C]
↑
main
git reset --hard HEAD~1
. Always be careful when using git reset —hard. It’s a
powerful tool, but it’s also a dangerous one.
Undo last commit. If you want to undo the commit, but keep your changes:
git reset HEAD~1
Go 2 commits back.
git reset --hard HEAD~2
Checkout the current branch, and rebase from <remote>
git reset --hard <remote>/<branch>
Apply only the changes made within a given commit. This is different to the merge
command, as it would otherwise apply all commits from a branch.
git cherry-pick [HASH]
Synchronize changes
“Authoritative source of truth” repo, we mean that it’s the one you and your
team treat as the “true” repo. It’s the one that contains the most up-to-date
version of the accepted code. In Git terminology this is ==origin
== (single
word).
Get the latest changes from origin (no merge)
git fetch
. This downloads copies of all the contents of the .git/objects
directory (and other bookkeeping information) from the remote repository into
your current one.
Pull changes, while overwriting any local commits.
Fetch the latest changes from origin and merge
git pull [<remote>/<branch>]
Fetch the latest changes from origin and rebase
git pull --rebase
Let’s assume we have a branch feature_branch
that we want to merge into main
.
How this graph looks like after we rebase feature_branch
on top of main
?
Is rebase a public branch (like main
) onto some other branch is good idea?
You should never rebase a public branch (like main
) onto anything else. Other
developers have it checked out, and if you change its history, you’ll cause a
lot of problems for them. In main we can make some updates, and if we rebase them into feature branch it can be completely broken (not expecting such changes).
Pull down a remote branch, but rebase any locally differing commits onto the top of the incoming commits:
git pull <remote> <branch> --rebase
Push local changes to the origin
git push
To force a push, when you know what you’re doing!
git push -f
Push to the tracked main branch.
git push origin main
Push a local branch to a remote with a different name.
git push origin <localbranch>:<remotebranch>
Push to a specified repository.
git push [email protected]:[USER_NAME]/[REPO_NAME].git
Git stash
Stash changes locally. This will keep the changes in a separate changelist, - called ‘stash’, and the working directory is cleaned. You can apply changes from the stash at any time.
git stash
Stash changes with a message.
git stash push accepts the message with option -m and accepts a list of files to stash as arguments.
git stash push -m <message>
List all the stashed changes.
git stash list
Apply the most recent change and remove the stash from the stash list.
git stash pop
Apply stash from the stash list, but does not remove the stash from the list.
git stash apply stash@{6}
Git remote
How add remote repo, can we use relative path?
Use this command to add a remote repo:
git remote add <name> <uri>
Yes you can use relative path, like ../some_other_repo/
.
How to list remote origin?
git ls-remote
How to merge remote repo into local repo?
git merge remote/branch
. For example: git merge origin/main
.
Remove all stale branches; ones that have been deleted on remote. So if you have
a lot of useless branches, delete them on GitHub and then run this.
git remote prune origin
Prune all remotes at once.
git remote prune $(git remote | tr '\n' ' ')
Configuration
Set your identity with git config
globally?
List local or global configurations using git config
?
Actually you are able to store and retrieve custom configuration in git config
file, how to do it?
How to remove a configuration key/value pair, for example local foo.bar
?
How to complete remove configuration section?
The GPG key used for signing your commits
git config --global user.signingkey 0A46826A
Set signing of commits globally
git config --global commit.gpgsign true
Set your editor.
git config --global core.editor nvim
Enable color support for commands like git diff
.
git config --global color.ui true
Which git configuration locations you know? How they override each other?
Here locations, priority from low to high:
- System:
/etc/gitconfig
- Global for user:
~/.gitconfig
, overrides system - Local for repository:
.git/config
, overrides global - Worktree:
.git/config
, overrides local
gitignore
What should you ignore in typical coding project?
- Ignore things that can be generated (e.g. compiled code, minified files, etc.)
- Ignore dependencies (e.g. node_modules, venv, packages, etc.)
- Ignore things that are personal or specific to how you like to work (e.g. editor settings, but not code assignments config)
- Ignore things that are sensitive or dangerous (e.g. .env files, passwords, API keys, etc.)
node_modules
vs node_modules/
in .gitignore
file, what is difference?
First one ignore directory name or file name as a “section”, second one ignore
directory only
"node_modules"
--------------
node_modules/code.js
src/node_modules/code.js
src/node_modules
"node_modules/"
--------------
node_modules/code.js
a/node_modules/code.txt
A nested .gitignore
file (in multiple directories) only applies to the
directory it's in and its subdirectories.
Remove file from git after adding it into .gitignore
You can add comments to your .gitignore file by starting a line with a ==#
==.
Ignoring single files
example.txt
examples/example.txt
Keeping single files (pattern negation)::bash !example.txt
Ignore multiple files with the same extension::*.txt
, *
wildcard pattern
Ignoring files only in the root directory
Must include a slash in the beginning /example.txt
Ignore multiple files with the same name (prefix)::example*
Ignoring files in every directory::bash **/example.txt
Ignores files named Example.txt
and example.txt
::[Ee]xample.txt
Other
List files changed in a given commit.
git diff-tree --no-commit-id --name-only -r [HASH]
Revisions can also be identified with :/text
. So, this will show the first
commit that has the string “cool” in its message body.
git show :/cool
List files changed in a given commit; user-facing approach.
git show --pretty="" --name-only bd61ad98
See everything you have done, across branches, in a glance, then go to the place right before you broke everything.
git reflog
git reset HEAD@{hash}
To revert first/initial commit on a branch:
Running git reset —hard HEAD~1 will give error:
fatal: ambiguous argument 'HEAD~1'
: unknown revision or path not in the working tree.
git update-ref -d HEAD
To import commits from another repo:
git --git-dir=../some_other_repo/.git format-patch -k -1 --stdout <commit SHA> | git am -3 -k
Update all submodules.
git submodule update --init --recursive
Check any signatures it finds and list them in its output:
git log --pretty="format:%h %G? %aN %s"
Sync a fork with the master repo.
Change the date of an existing commit.
Display the commit history of a set of files.
git log --pretty=email --patch-with-stat --reverse --full-index -- Admin\*.py > Sripts.patch
Move your most recent commit from one branch, to stage it on [BRANCH].
How to implement typical and simple Git workflow with pull-requests and feature branches,
main steps?
- Update my local main branch with
git pull [origin main]
- Checkout a new branch for the changes I want to make with
git switch -c <branchname>
- Make changes to files and commit
- Push feature branch with MR create flag
git push -o merge_request.create origin <branchname>
, or just push and create MR through Web UI. - Ask a team member to review my pull request
- Once approved, click the “Merge” button on GitHub to merge my changes into main
- Delete my feature branch, pull
main
, and repeat with a new branch for the next set of changes
Merge Conflicts
When merge conflicts happens?
Merge conflicts happen when you merge branches that have competing commits, and
Git needs your help to decide which changes to incorporate in the final
merge. Often, merge conflicts happen when people make different changes to the
same line of the same file, or when one person edits a file and another person
deletes the same file.
Merge conflict example, which lines are referring to remote changes?
If you have questions, please
<<<<<<< HEAD
open an issue
=======
ask your question in IRC.
>>>>>>> branch-a
Second chunk (after =======
).
Conflict markers <<<<<<<
- local changes from HEAD, =======
changes divider,
>>>>>>>
remote changes from branch-a
.
What if there merge conflict with removed file, someone removed file, but you
have done some changes to it, what to do?
You can add this file with git add
or remove with git rm
and resolve merge
conflict.
External links
-
Resolving a merge conflict using the command line - GitHub Docs
-
GitHub - conventional-commits/conventionalcommits.org: The conventional commits specification
-
The ULTIMATE Git workflow using Neovim’s Fugitive, Telescope & Git-Signs!
-
arslanbilal/git-cheat-sheet: :octocat: git and git flow cheat sheet
-
git index - Git - Difference Between ‘assume-unchanged’ and ‘skip-worktree’ - Stack Overflow
-
bennadel/git-cheat-sheet: A small git cheat sheet of common use-cases for my future self.
-
github - How to remove file from Git history? - Stack Overflow
-
git filter-repo --invert-paths --path <path to the file or directory>