- index - staging area
States:
- untracked - not tracked ( not in last snapshot )
- unmodified - not changed
- modified - changed but not committed
- staged - marked for next commit
- committed - added to DB
Install
Linux:
sudo dnf install git-all
sudo apt install git-all
MacOS Install:
- install Xcode Command Line Tools
- run git from cli for the first time
Alternate install
- Directly download binaries
Check version:
git --version
Configure
[path]//etc/gitconfig
~/.gitconfig or ~/.config/git/config
.git/config
git config --list # check settings
git config --list --show-origin # view all settings and where they are from
--global # set for all projects
git config --global user.name "John Doe"
git config --global user.email johndoe@example.com
git config --global core.editor emacs
git config --global core.editor "'C:/Program Files/Notepad++/notepad++.exe' -multiInst -notabbar -nosession -noPlugin"
git config --global init.defaultBranch main # change default branch name instead of master
git config user.name
git config core.editor
git config --show-origin user.name
Get Help
git help xxxxx
git xxxx --help
git help config
Basic Commands
cd /home/user/my_project
git init
git add *.c
git add LICENSE # version at time you ran this command
git commit -m 'Initial project version'
git clone https://github.com/libgit2/libgit2
git clone https://github.com/libgit2/libgit2 mylibgit
Adding files:
git add xxxx
# version at time you ran this command, changing again will result in stated/unstaged status
# begin tracking, stage, mark merge-conflicted files as resolved
Status
- Shows status and what hasn’t been pushed.
- Doesn’t show what hasn’t been pulled.
git status # check status
- clean - tracked files are unmodified, no untracked files
git status -s # short status
M modified, not staged ( note the leading space )
M modified, staged
MM modified, staged, modified
A new, staged
?? untracked
- left char - staging area status
- right char - working tree status
Git Ignore
.gitignore*.[oa] *~ /!doc.txt # negate, do track this one *.txt # glob /*.txt # leading slash for non recursive build/ # slash to match dir a/**/z # match nested dirs, multiple levels doc/*.txt # not recursive doc/**/*.pdf # recursive
- multiple .gitignore files can be placed in sub dirs
Diff / Compare
Standard diff:
git diff # compare working dir to staged
git diff --staged # compare staged to last commit
git diff --cached # compare staged to last commit
Tools:
git difftool --tool-help
git difftool --tool=araxis
git difftool --tool=kompare
Commit
git commit # commit, open editor with output of 'git status' commented out, comments stripped after saving
git commit -v # same but with 'git diff'
git commit -m "my update 1 - some changes" # specify commit message
git commit -a -m 'Add new benchmarks' # add all tracked files and commit
Remove / Delete
git rm test1.java # remove from staging and working dir ( removes from git )
git rm -f test1.java # force if it was already added to staging
git rm --cached output.log # remove from staging ( and from git ) but keep in working dir
# good for accidentally included files that should be ignored
git rm log/\*.log # !!! escape * so we can use git expansion and not shell expansion
Rename
Rename like this:
git mv README.md README
Or this, git will figure out what was renamed:
mv README.md README
git rm README.md
git add README
Log / History
git log # show commit history
git log -p -2 # show diff / patch output, limit to 2 entries
git log --stat # short stats for each commit
git log --pretty=oneline # one line output
git log --pretty=format:"%h - %an, %ar : %s"
git log --pretty=format:"%H - %an, %cd : %s"
git log --pretty=format:"%h %s" --graph # branch and merge history
git log --since=2.weeks
git log --since=2024-01-01
git log --pretty=oneline --since=2024-01-01
git log -S function_name # Git’s “pickaxe” option - commits that changed occurrences of string
git log --_includes/common_header1.md # commits that changed this file
git log --pretty=oneline -- _includes/common_header1.md # same but one line
Git log formatting specifiers:
Specifier | Description of Output |
%H | Commit hash |
%h | Abbreviated commit hash |
%T | Tree hash |
%t | Abbreviated tree hash |
%P | Parent hashes |
%p | Abbreviated parent hashes |
%an | Author name |
%ae | Author email |
%ad | Author date (format respects the –date=option) |
%ar | Author date, relative |
%cn | Committer name |
%ce | Committer email |
%cd | Committer date |
%cr | Committer date, relative |
%s | Subject |
Git log options:
Option | Description |
-p | Show the patch introduced with each commit. |
–stat | Show statistics for files modified in each commit. |
–shortstat | Display only the changed/insertions/deletions line from the –stat command. |
–name-only | Show the list of files modified after the commit information. |
–name-status | Show the list of files affected with added/modified/deleted information as well. |
–abbrev-commit | Show only the first few characters of the SHA-1 checksum instead of all 40. |
–relative-date | Display the date in a relative format (for example, “2 weeks ago”) instead ofusing the full date format. |
–graph | Display an ASCII graph of the branch and merge history beside the log output. |
–pretty | Show commits in an alternate format. Option values include oneline, short,full, fuller, and format (where you specify your own format). |
–oneline | Shorthand for –pretty=oneline –abbrev-commit used together. |
–all-match | and instead of or |
-<n> | Show only the last n commits. |
–since, | –after Limit the commits to those made after the specified date. |
–until, | –before Limit the commits to those made before the specified date. |
–author | Only show commits in which the author entry matches the specified string. |
–committer | Only show commits in which the committer entry matches the specified string. |
–grep | Only show commits with a commit message containing the string. |
-S | Only show commits adding or removing code matching the string. |
–no-merges | don’t show merge commits |
Amend
Change existing commit:
git add . # add forgotton files / missing changes
git commit --amend # ammend to commit ( completly replaces that commit )
Use amend before pushing. Using amend and then force pushing can cause issues for others. ( ? probably fine in a forked setup ? )
Undoing Changes
git reset HEAD CONTRIBUTING.md # unstage a file
git restore --staged CONTRIBUTING.md # unstage a file (new command in 2.23.0 )
git checkout -- CONTRIBUTING.md # replace file in working dir with last staged or committed version
git restore CONTRIBUTING.md # replace file in working dir with last staged or committed version
Remotes
git remote # show remote servers ( short names )
git remote -v # also show URLs
git remote add pb https://github.com/paulboone/ticgit # shortname, URL
git remote show origin # inspect remote, shows which branches are tracked and how they map out
git remote rename pb paul # rename a remote
git remote remove paul # remove a remote
git remote rm paul # remove a remote
- origin - default name for server that repo was cloned from
- local master is setup to track orgin master ( or default branch ) when cloning remote repo
Fetch and Pull
git fetch # get all info, you can then use other branches that you didn't have
git fetch pb # get all info from this remote
git fetch origin # get all info from this remote
git pull # fetch and merge into current branch ( usually from origin master )
Push
git push origin master
git push
Rebase Config
git config --global pull.rebase "false"
* default behavior of Git (fast-forward if possible, else create a merge commit)
* 2.27 might give warnings about pull.rebase not being set on git pull
Tags
git tag # list tags
git tag -l "v1.8.5*" # list matching tags
- lightweight tags - just a pointer to a commit
- annotated tags - full objects with a bunch of info ( recommended )
git tag -a v1.4 -m "my version 1.4" # create annoteted tag, -m to specify message
git show v1.4 # show it
git tag v1.4-lw # create lightweight tag
git show v1.4-lw # show it
git tag -a v1.2 9fceb02 # tag existing commit
git push origin v1.5 # push a tag to remote
git push origin --tags # push all tags
git push origin --follow-tags # only annoteted tags
git tag -d v1.4-lw # delete local tag
git push origin :refs/tags/v1.4-lw # delete remote tag ( overwrite with null value )
git push origin --delete v1.4-lw # delete remote tag
git checkout v2.0.0 # checkout a tag ( but results in detached HEAD )
git checkout -b version2 v2.0.0 # create a branch based on a tag
Aliases
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.st status
git config --global alias.unstage 'reset HEAD --'
git config --global alias.last 'log -1 HEAD'
git config --global alias.visual '!gitk' # ! for external command
Brancing
- HEAD - pointer to current branch
- master - default main branch
- main - other default main branch
git branch # list branches
git branch -v # list branches with last commit
git branch --merged # show branches that are already merged into current branch
git branch --no-merged # not merged yet
git branch --no-merged master # what isn't merged into master
git branch testing # create new branch pointing to current commit, DOESN'T switch to it
git checkout testing # switch to existing branch
git checkout master # switch back
git checkout -b test2 # create and switch to new branch
git switch testing # switch to existing branch ( v2.23 and up )
git switch -c test2 # create and switch to branch
git switch - # switch to previous checked out branch
More branch info
git log --oneline --decorate # show where branch pointers are pointing
git log --oneline --decorate --graph --all # show graph of all branches
git log testing # show history for this branch
git log --all # show history for all branches
Merging
- Fast forward - if there is a direct chain, just moves pointer forward
-
Recursive strategy - three way merge - 1 common ansestor and 2 branch tips, results in new commit ( merge commit - has more than one parent )
- Fast-forward merge - just move pointer forward
- Recursive merge - 3 way merge: 1 common ancestor, 2 branch tips
- Octopus merge - multiple branches all merged at once
git checkout master
git merge hotfix # hotfix merged into master, both will point to same commit
git branch -d hotfix # remove branch
git branch -D hotfix # remove branch that hasn't been merged
Merge Conflict
git status # show which files are unmerged ( Unmerged paths: )
Manually edit each section in each file that looks like this:
<<<<<<< HEAD:index.html
size = 10
=======
size = 5
>>>>>>> hotfix:index.html
Next:
git add index.html # add each file that was manually edited
git commit # finish the merge
Optionally, to edit and add:
git mergetool # fire up a gui tool, can probably edit and add
Cancel a merge in progress:
git merge --abort
Always commit or stash before merging, aborting a merge could result in lossing uncommitted files.
git branch --move bad-branch-name corrected-branch-name # rename branch locally
git push --set-upstream origin corrected-branch-name # push renamed branch to remote
git push origin --delete bad-branch-name # remove old branch name from remote
git branch --all # show all branches
git branch --move master main # rename master to main, can break things
git push --set-upstream origin main
git push origin --delete master
Branching Workflows
Long-Running Branches
ex:
- master - stable / prod
- dev - unstable /dev
Topic branches ( short lived branches )
- topic branches can be merged into dev and dev can be merged into master
ex:
- topic/silly_bug_fix
Commit History
Commit history is still branched after removing branch:
$git log --oneline --decorate --graph --all
* 9e54cb2 (HEAD -> master) Merge branch 'topic/abc123'
|\
| * 59cfb94 (topic/abc123) update
* | 595b1af update
|/
* 25e7897 update
* a3a00e1 update
* a7d16a5 update
$git branch -d topic/abc123
Deleted branch topic/abc123 (was 59cfb94).
$git log --oneline --decorate --graph --all
* 9e54cb2 (HEAD -> master) Merge branch 'topic/abc123'
|\
| * 59cfb94 update
* | 595b1af update
|/
* 25e7897 update
* a3a00e1 update
* a7d16a5 update
$
Git show remote references:
git ls-remote repo-name
git remote show remote-name
Remote Branches
- tracking branch - tracks an upstream branch (?? used as separate term??? )
-
Remote-tracking branches - RO pointers to remote branches so you can track them
- Look like this: remote/branch
Remote branches:
git push origin serverfix # push a branch to remote
git push origin serverfix:awesomebranch # use different branch name on remote
git merge origin/serverfix # merge remot branch with current local
git checkout -b serverfix origin/serverfix # create local branch from remote branch and tracks it
git checkout --track origin/serverfix # same
git checkout -b sf origin/serverfix # use different local name
git branch -u origin/serverfix # track specific remote branch on current local ( or change the tracked branch )
git merge @{u} # shortcut for remote branch
git branch -vv # show what each branch is tracking ( since last fetch )
git fetch --all;
git branch -vv
git pull # fetch and merge current local branch with current remote
git push origin --delete serverfix # delete remote branch
Credential Helpers:
git config --global credential.helper cache # temp 15 min cache
git config --global credential.helper 'store --file ~/.my-credentials' # clear text in file
Rebasing
git checkout experiment
git rebase master # pull in all changes from master and apply them before any changes in experiment
git checkout master # back to master
git merge experiment # can now do a fast forward merge
rebase:
- save all changes that were made to current branch ( diffs between HEAD and common ancestor )
- move HEAD back to common ancestor commit
- add all changes from other branch
- add changes for current branch back in
- afterwards, can merge with fast forward for a linear history
Make sure your commits apply cleanly on a remote branch ( good if someone else maintains it ).
Example
- Take client branch changes since it diverged from server branch
- Replay those on top of the current master branch
- The point the client branch to point to the tip of that
- Then merge ( fast forward ) master to this
git rebase --onto master server client
git checkout master
git merge client
- Rebase server onto master without having to check it out
- Then merge ( fast forward ) that too
git rebase master server
git checkout master
git merge server
Remove the topic branches:
git branch -d client
git branch -d server
Cancel Rebase
git rebase --abort # cancel rebase
git rebase --quit # if cancelling didn't work
The Perils of Rebasing
“Do not rebase commits that exist outside your repository and that people may have based work on.”
Scenario:
- Someone else pulls your branch before you rebase
- You rebase and force push
- They push it back up after you rebase
- It will include your old changes and your rebased version of those changes ( duplicate commits ).
- Keeps getting more messed up from there.
I’ve you have someone elses rebased work, you can rebase again and git should figure out which commits are duplicates and which need to be added ( rebase on top of force pushed rebase ).
git fetch
git rebase teamone/master
OR
git pull --rebase
Rebase vs Merge
- Rebase - cleaner, not a “first draft”, more coherent story
- Merge - preserve history, how it actually happened
Git Revert
git revert 25e7897d1ce4ca817555d71ac158d8e7112bded5 # revert a commit ( just changes from this commit ), creates new commit for this
git revert HEAD # revert last commit in current branch
git reset 25e7897d1ce4ca817555d71ac158d8e7112bded5 # revert to this point and any other commit after it ( HEAD goes back to this commit )
Squashing
Squashing with merge:
git merge --squash
Squash local commits:
git rebase -i
Squash to whatever is in origin/master:
git rebase -i origin/master
git push -f
If there are conflicts:
git add .
git rebase --continue
Cherry Pick
git cherry-pick e43a6 # pull a specific commit from any other branch into your HEAD branch
git cherry-pick af02e0b --no-commit # only add to current copy
Git Stash
“Stash the changes in a dirty working directory away”
git stash # stash uncommitted changes ( staged and unstaged, not untracked )
git stash -u # include untracked files
git stash list # Show stashes
git stash show # show operations performed on the stash
git stash save "message" # message for this stash (deprecated)
git stash push "message" # message for this stash
git pop # remove changes from stash, restore to working dir and index
git apply # keep in stash but also restore to working dir and index
git stash branch newbranch # create new branch based on stash and the commit it was based on, latest stash
git stash branch newbranch stash@{0} # same but specify the stash
git stash drop stash@{1} # remove a stash
git stash clear # remove all stashes
Git Server
bare repository — git repo with no working directory
Protocols: Local, HTTP, Secure Shell (SSH) and Git
local
local - local file system ( or NAS )
git clone /srv/git/project.git # tries to use hard links and file copying
git clone file:///srv/git/project.git # transfers using network
git remote add local_proj /srv/git/project.git # add local repo to existing project as a remote
HTTP
- need web server, and some other steps
Smart HTTP # pre 1.6.6 username/password authentication anonymous or with encryption and auth Dumb HTTP # 1.6.6 and later
SSH
- requires key setup ( probably )
- requires ssh access to host ( might not want this for public stuff but fine on home or corporate networks )
- comes with encryption and auth
Clone over SSH:
git clone ssh://[user@]server/project.git
git clone [user@]server:project.git
Git Protocol
- no security
- no encryption
- no auth
-
port 9418
- git daemon can serve this
- usually no push access but can be enabled
- supposed to be fast, same transfer mechanism as SSH but no auth or encryption
git-daemon-export-ok - file must exist in repo for daemon to serve the repo
git clone git://example.com/project.git
References
Most things on this page are based on this book: