Git
Show diff between a revision and its parent
These two commands are equivalent:
$ git diff <REV>^!
$ git diff <REV>~..<REV>
Show diff between different files in different branches
git difftool -d REV1:path/to/file1 REV2:path/to/another/file2
Difference between ..
and ...
..
: used with git log
E.g.:
$ git log r1..r2
means "list of commits logs in branch r2, but exclude commits in r1" Useful when r2 is a branch of r1.
...
: used with git diff
E.g.
git diff r1...r2
means "show diff in BOTH r1 and r2 from the common ancestor of r1 and r2"
Create a patch from a commit / apply it
Create patch e.g. from the last commit:
$ git format-patch -1 HEAD
Apply patch with:
$ git am < file.patch
$ git am --3way 000x-...-file.path
Use --3way
to generate 3-way diff stuff when patching fails.
rev-parse
Get (short) commit hash of HEAD
$ git rev-parse HEAD
$ git rev-parse --short HEAD
Pull from a forced update (push --force)
$ git stash # if needed
$ git fetch
$ git reset remote/branch --hard
$ git rebase -i remote/branch
GitHub: checkout a PR locally
Given a PR identified by ID, you can fetch it a local branch with name <branch-name>
NOTE:
<local-branch>
can be any name!
$ git fetch origin pull/<PR ID>/head:<local-branch>
$ git checkout <branch-name>
If doing any modification, you can also push with:
git push origin <branch-name>
Git describe
Find the most recent tag that is reachable from a commit:
$ git describe [<commit id>]
When no commit id is given, default is HEAD.
Output:
<tag name>-<number of commits since last tag>-<most recent commit id>
Add single hunks interactively
$ git add -i
- Then pres
4
OR just:
$ git add --patch
Git rebase
Use rebase
for:
git rebase -i master
: move a set of commits on the top, instead of merging.git rebase -i COMMIT-ID
: edit/squash a set of commits (pick, drop, reword commit message, squash).- To rebase from a common ancestor, use
git merge-base HEAD master
$ git pull --rebase <mybranch>
--autosquash
: automatically set a fixup!
commit to be a fixup instead of a pick.
Can be enabled by default with the config: rebase.autoSquash
See: https://www.atlassian.com/git/tutorials/rewriting-history/git-rebase
Manage submodules
Add submodules to existing repo:
$ git submodule add https://github.com/foo/bar.git
Clone a repo and at the same time populate submodules:
During clone:
$ git clone XXXX --recursive
Or after cloning:
$ git submodule update --init --recursive
Amend: edit the last commit message OR add new changes to last commit
$ git commit --amend <filename>
See: https://www.atlassian.com/git/tutorials/rewriting-history
Specifying a Git revision
HEAD
: most recent commit in the current branch
<REV>
, <REV>~0
, <REV>^0
: the revision itself
<REV>~
, <REV>~1
: previous commit of revision <REV>
(if multiple parents, 1st parent)
<REV>~n
: previous n commit of revision
<REV>^
, <REV>^1
: 1st parent of <REV>
<REV>^n
: nth parent of <REV>
<REV>^!
: equivalent to <REV>~..<REV>
git rev-parse
for more info!
Specifying a Git refspec
For more syntax see gitrevisions(7)
.
-
@{1}
: the state of the branch one step back in the reflog - e.g. before a rebase operation -
@{u}
: the upstream branch (e.g. for branchfoo
,origin/foo
) -
@
: alias forHEAD
, same asHEAD@{1}
.
Delete sensitive data
Use the bfg tool
$ bfg --replace-text sensitive_text_list.txt
NOTE: if hosting remotely (e.g. GitHub), the remote repo need to be deleted and re-created!
Undo the last commit
(But keep changes)
$ git reset HEAD~
Unstage staged / added files
(But keep changes)
$ git reset HEAD
Show
Show changes done in a commit
Equivalent to git difftool -d REVISION^!
:
$ git show REVISION
Show a specific file revision
$ git show REVISION:./path_of_file
Delete a branch
First, check out any branch != branch_to_delete:
$ git checkout master
$ git branch -d branch_to_delete
$ git push origin --delete branch_to_delete
Remove any remote tracking branches that no longer exist remotely:
$ git fetch --prune
Push from a local branch to another upstream branch
ID can be HEAD
, a branch name, a commit hash, ... :
$ git push [remote-name] <ID>:<upstream-branch-name>
# ^
# |____ use ":"
$ git branch --contains=HEAD
foo
# Push HEAD in foo to remote branch bar
$ git push origin HEAD:bar
# Push branch foo to remote branch bar
$ git push origin foo:bar
Common ancestor
Find as good common ancestors as possible for a merge:
$ git merge-base COMMIT1 COMMIT1
Handy to be used with rebase:
$ git rebase -i $(git merge-base HEAD origin/master)
Really clean everything
NOTE: USE WITH CARE!
$ git clean -dffx
Checkout a branch with an arbitrary name
$ git checkout -b LOCAL-BRANCH origin/remote-branch-name
Ignore files without changing .gitignore
Add file paths to either:
a. .git/info/exclude
- ignore files only in a given repo.
b. ~/.config/git/ignore
- ignore files in any repo on this system.
Prevent changes to a tracked file to show up in git status
Add this to your config:
[alias]
ignore = update-index --assume-unchanged
unignore = update-index --no-assume-unchanged
ignored = "!git ls-files -v | grep ^[a-z]"
and use git ignore changed-file
Fetching an untagged (orphaned) commit ID
Git does not fetch orphaned commit IDs, i.e IDs not reachable by name. You first need to tag tag commit ID upstream.
Fetch & update local branch
This way, you don't need to git checkout
and git pull
.
$ git fetch origin BRANCH-NAME:BRANCH-NAME
Fetch a remote revision not associated to a branch
$ git fetch origin abcdef012345678900000000000000000000000
Rewrite previous commits username and emails
Via this change-commits
alias:
git config --global alias.change-commits '!'"f() { VAR=\$1; OLD=\$2; NEW=\$3; shift 3; git filter-branch --env-filter \"if [[ \\\"\$\`echo \$VAR\`\\\" = '\$OLD' ]]; then export \$VAR='\$NEW'; fi\" \$@; }; f"
E.g., change the author:
git change-commits GIT_AUTHOR_NAME "old name" "new name"
change email for the last 10 commits:
git change-commits GIT_AUTHOR_EMAIL "[email protected]" "[email protected]" HEAD~10..HEAD
List of commit envs:
GIT_AUTHOR_NAME is the human-readable name in the “author” field.
GIT_AUTHOR_EMAIL is the email for the “author” field.
GIT_AUTHOR_DATE is the timestamp used for the “author” field.
GIT_COMMITTER_NAME sets the human name for the “committer” field.
GIT_COMMITTER_EMAIL is the email address for the “committer” field.
GIT_COMMITTER_DATE is used for the timestamp in the “committer” field.
Clone
-
Only a single branch or tag: use
--single-branch --branch <BRANCH-NAME | TAG-NAME>
-
Shallow clone: use
--depth
. E.g.--depth 1
will only clone the latest revision.
Combined:
git clone \
--depth 1 \
--branch Ubuntu-aws-5.4-5.4.0-1041.43_18.04.1 \
--single-branch \
git://git.launchpad.net/~canonical-kernel/ubuntu/+source/linux-aws/+git/bionic
DEST-FOLDER
Switch to branches
You can use git switch BRANCH-NAME
instead of git checkout ...
.
Restore files
You can use git restore --file ./PATH/TO/FILE
instead of git checkout -- ./PATH/TO/FILE
.
Stash
You can stash only staged changes with git stash --staged --message "only stashing staged stuff"
.
Jump
You can use the git-jump
extension to jump to interesting stuff in an editor:
- merge: git jump merge [-- file/path]
- diff
- grep
Fancy diff
Use --color-words
option to show changed words in a line in red / green color,
instead of seeing the same line before / after the change.
Short status
git status -s
Shortened Git log hash
git log --abbrev-commit
range-diff
Diff between ranges of commits
Let's say you have rebased your branch with N commits, and you want to prove
your changes done for rebase are correct. You can use range-diff
to do that.
git [--word-diff] [--color-words] range-diff BEFORE_REBASE_HEAD~N..BEFORE_REBASE_HEAD HEAD~N..HEAD
Use either the --word-diff
or --color-words
option to make the diff easier
to read.
Compare before/after rebase
When a rebase required merge conflicts to be resolved, compare the changes introduced by the rebase directly afterwards using:
$ git range-diff @{u} @{1} @
or, more readable:
$ git range-diff @{upstream} @{1} HEAD
The syntax <base> <rev1> <rev2>
is equivalent to <base>..<rev1> <base>..<rev2>
To understand the syntax, see the "refspec" section above.