✂️ Git

Small information nuggets and recipies about Git


See also:

So here are some bad situations I’ve gotten myself into, and how I eventually got myself out of them in plain english.

Oh Shit, Git!?!


(most recent on top)

Configure separate Git users for different repos

~/.gitconfig

[user]
  name = First Last
  email = [email protected]

[includeIf "gitdir:~/work/"]
  path = ~/work/.gitconfig

~/work/.gitconfig

[user]
  email = [email protected]

Revert changes of a single file of a commit

git show <commit> -- some_file.c | git apply -R

Manually ignore changes to a tracked file or directory

… stop tracking

git update-index --assume-unchanged <file>

… resume tracking

git update-index --no-assume-unchanged <file>

Rebase a topic branch into anywhere you want

topic info master (run from any branch)

git rebase master topic                     # default is HEAD of master
git rebase master topic --onto commit

Change the committer and author names

… set default repo user

git config user.name "User Name"
git config user.email [email protected]

… author = default, committer = default

git commit --amend --no-edit --reset-author

… author = custom, committer = default

git commit --amend --no-edit --author "Author Name <[email protected]>"

… author = same, committer = custom

git -c user.name="Committer Name" -c user.email=[email protected] commit --amend --no-edit

… in many commits

git rebase -i -p
# mark as: edit
# repeat as above

Fixup commits and autosquash rebases

… create

# squash fixed commit; prefixes message with "squash! "
git commit --squash e37dc68
# fixup relative commit; prefixes message with "fixup! "
git commit --fixup @~1
# fixup commit with string; prefixes message with "fixup! "
git commit --fixup :/foobar

… apply

# auto reorganises the squash/fixup and moves them to the right place
git rebase -i --autosquash
# makes it the default for all rebases
git config --global rebase.autosquash true

Find broken commit

git bisect start <bad> <good>

… example

git bisect start @ origin/master
git bisect run bin/test.sh

Detect which folders are affected by the latest commits (monorepo)

git diff-tree --no-commit-id --name-only HEAD tag

Delete remote branch or tag

… as of v1.7

git push origin --delete <branch_name>
git push origin --delete <tag_name>

… before that

git push origin :<branch_name>

Differences between local and upstream

git diff @{u}..@

The @ sign is an alias for HEAD

git show @
git show @~2

Change a commit original author date

… set author date

git commit --amend --no-edit --date="Sun Dec 31 12:34:56 2017"

… set committer date + author date

GIT_COMMITTER_DATE="Sun Dec 31 23:45:00 2017" git commit --amend --no-edit --date="Sun Dec 31 23:45:00 2017"

… set committer date = author date

GIT_COMMITTER_DATE="Mon May 7 23:54:$(echo $((RANDOM % 50 + 10)) | cut -c-2) 2018" bash -c 'git commit --amend --no-edit --date="$GIT_COMMITTER_DATE"'

… random seconds

echo $((RANDOM % 50 + 10)) | cut -c-2

… see also custom function shell git-commit-dates()

Rebase commits to reset commit date to the original author date

git rebase --committer-date-is-author-date @~3

Auto stash dirty directory on pull/rebase

… autostash is only available since git version 2.9

git pull --rebase --autostash

… for earlier versions use:

git config --global rebase.autoStash true

Check lightweight vs. annotated tags

annotated: “You can see the tag data along with the commit that was tagged by using the git show command”

$ git show annotated-tag
tag annotated-tag
Tagger: Hugo Ferreira <[email protected]>
Date:   Thu Dec 24 10:54:15 2015 +0000

message text with details

commit 6ec10ad250c8bbbdc42dba51a09c02b456e02187 (tag: annotated-tag)
Author: Hugo Ferreira <[email protected]>
Date:   Thu Dec 24 10:23:50 2015 +0000

lightweight: “This time, if you run git show on the tag, you don’t see the extra tag information. The command just shows the commit”

$ git show light-tag
commit f5d32c57c9612c610dc0c45cc0ff3f0b1d21fd53 (HEAD -> master, tag: light-tag)
Author: Hugo Ferreira <[email protected]>
Date:   Thu Dec 24 10:25:44 2015 +000

Set user & email info

… current repo only .git/config

git config user.name "Hugo Ferreira"
git config user.email [email protected]

… default for all repos ~/.gitconfig

git config --global user.name "Hugo Ferreira"
git config --global user.email [email protected]

Prepare a patch formatted for email

git format-patch HEAD~1
git format-patch <sha>..@

Apply a patch received as file

… dry-run

git apply --check --stat 0001-file.patch

… apply and create commits

git am 0001-file.patch

… apply patch to files

# files only
git apply 0001-file.patch
# files + index
git apply --index 0001-file.patch
# index only
git apply --cached 0001-file.patch

Show files being ignored

git clean --dry-run -dX
git clean -ndX

Choose a new root commit for the repo

git rebase -i --root

Visualise the entire commit graph

git log --graph --oneline --all --color --decorate

Rollback the last commit for good

git reset HEAD~1 --hard

Undo a rebase

… show the history of operations (like an “undo” stack)

$ git reflog
b710729 HEAD@{0}: rebase: some commit
5ad7c1c HEAD@{1}: rebase: another commit
deafcbf HEAD@{2}: checkout: moving from master to my-branch

… reset to the commit before the rebase

git reset --hard deafcbf

Recover lost commits

… discover

git fsck --lost-found
git reflog
git show <sha1>

… restore

git cherry-pick <sha1>
git merge <sha1>

Show outgoing changes

git fetch && git whatchanged origin..

Show incoming changes

git fetch && git whatchanged ..origin

git-flow rules of engagement for releases (merge commit) messages

… setup

git config --global merge.log true

… default message

Merge branch 'release/1.0.0'

* develop:
  One commit message
  Another commit message

… edit: commit message (merge)

Merge branch 'release/1.0.0'

- One commit message
- Another commit message

… edit: tag message (releases)

release/1.0.0

- One commit message
- Another commit message