Git tips
Contents |
Git commands
Add and commit in one command alias
git config --global alias.add-commit '!git add -A && git commit'
checkout remote branch and track it
git checkout -t origin/branch
Remove remote branch delete remote
git push origin --delete gh-pages
Push branch and track
git push origin development:development
Get current branch name
git branch --show-current
git symbolic-ref --short HEAD
Getting existing git branches to track remote branches:
git push --set-upstream origin my-branch
# but you can set
git config --global push.default current
# so you can simply
git push
Show latest branch ordered by updated time
git branch --sort=-committerdate
Show authors for each line, but skip some common refactoring. https://docs.github.com/en/repositories/working-with-files/using-files/viewing-a-file#ignore-commits-in-the-blame-view
# .git-blame-ignore-revs
# Removed semi-colons from the entire codebase
a8940f7fbddf7fad9d7d50014d4e8d46baf30592
# Converted all JavaScript to TypeScript
69d029cec8337c616552756310748c4a507bd75a
Git log
Show commits that are descendant of commit1 AND ancestors of commit2, in the same time
git log --reverse --ancestry-path commit1^..commit2
git log $(git merge-base HEAD branch)..branch # show commits only on branch
See all the changed code
git log -p
Also with adding
git add -p # e to open editor
Git stash only unstaged changes
git stash --keep-index
Git checkout previous branch, last branch, latest branch
git checkout -
# more robust way to reffer prev branch
git checkout @{-1}
When you want to undo some commands
git reflog
For work in progress you can use wip
git -m wip # one word does not need ""
Show file at specific revision
git show HEAD~4:index.html
Find difference of current file with some point
git diff 123123123 -- index.html
Find difference of all files and folders with meld diff tool
git difftool master --dir-diff
Find difference on this branch (from common ancestor) use three dots
git diff master...my_branch
Show only file names
git diff 134 --name-only
Copy files filename to files path
git diff --name-only > filelist.txt
tar cvf - -T filelist.txt | tar xvf - -C ~/target/folder
show only files that are added or modified
git diff 123 --diff-filter=AM
show only files that are NOT added and NOT modified
git diff 123 --diff-filter=am
show only sha or short sha
git rev-parse --short HEAD
Find bug using bisect on specific folder or file to jump half steps between
git bisect start -- app/controllers/registration_controller.rb
If you need to push to two remote repositories, you can do it in one command
git remote add all [email protected]:duleorlovic/tips.git
git remote set-url --add all [email protected]:duleorlovic_tips.git
git push all master --set-upstream
git push
To push to heroku without changes, try
git commit --allow-empty -m "empty commit"
git push heroku master
To sort authors by number of commits
git shortlog -e -s -n
# last month for each user
git log --since="last month" --format='%aN' | sort | uniq -c | sort -n
Versioning SEMVER
Multiple accounts
If there is two accounts on bitbucket or github with different keys you can use
host alias with those keys, defined in .ssh/config
. Let’s first generate
another key with ssh-keygen
and type the full path of the new key:
ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/dule/.ssh/id_rsa): /home/dule/.ssh/id_rsa_kulakajak
Than create a file with slighty different domain
# ~/.ssh/config
Host kgithub.com
Hostname kgithub.com
IdentityFile ~/.ssh/id_rsa_kulakajak
and now you can clone using new key and host kgithub.com
cat ~/.ssh/id_rsa_kulakajak.pub
# copy and paste this to https://github.com/settings/keys
git clone [email protected]:kulakajak/3d-slova.in.rs.git
Note that you need to use git
protocol url when seting remote (https
protocol does not use this mappings). To change current url you can edit
.git/config
or use command git remote set-url origin
[email protected]:company/repo
.
To see which username (-T) and wich key (-v) is using:
ssh -T [email protected]
ssh -v [email protected]
You can see which keys currenlty is used with ssh-add -L
. You can create new
session with ssh-agent bash -c 'ssh-add ~/.ssh/id_rsa_my_company_account; git
clone [email protected]:company/repo.git'
. But new session will not stop ssh for
offering other keys. To specificy which key to offer and disable others you can
ssh -v -i my_key deploy@$PRODUCTION_IP
If you want, you can generate id_rsa_secure with password, so it will asked each
time you use git command. Heroku uses keys from ~/.netrc
which is created
when you type heroku login
(once is setup, it stays forever)
Note that if you have a lot of keys than all them will be offered and you can received error before it asks for password
debug1: Authentications that can continue: publickey,password
debug1: Offering public key: RSA SHA256:bXvs6rHfIUJaL2iCeI/7HzEYTXrNEgydI5ufuK7r0mE orlovic@main
Received disconnect from 167.172.189.128 port 22:2: Too many authentication failures
Disconnected from 167.172.189.128 port 22
You can try to ssh localhost
and than ssh deploy@$PRODUCTION_IP
in which
case if will not offer all keys
debug1: Authentications that can continue: publickey,password
debug1: Trying private key: /home/orlovic/.ssh/id_dsa
debug1: Trying private key: /home/orlovic/.ssh/id_ecdsa
debug1: Trying private key: /home/orlovic/.ssh/id_ed25519
debug1: Next authentication method: password
Solution is to remove some public keys or to specify key with -i
option
To add hosts to to known_hosts so you do not see this warning
The authenticity of host 'github.com (140.82.121.3)' can't be established.
you can use
ssh-keyscan github.com >> ~/.ssh/known_hosts
Git commit
If you want to ignore/reignore some files that are tracked (gitignore does not
work on tracked files). Note that when you change branch with changes to that
files, error will be reported. Also note that git checkout .
or git stash
will be also applied to those hidden changes.
git update-index --assume-unchanged config/database.yml
# then to track changes again
git update-index --no-assume-unchanged config/database.yml
If you want to add folder to git structure but ignore all its content (like
backup
or tmp
folder) you can do with three steps
mkdir tmp
echo '*' > tmp/.gitignore
git add tmp/.gitignore -f
If you want to skip history and download latest code
git clone --depth=1 your-repo-url
Clone all repositories from same user or organization, for example twitter
curl -s https://api.github.com/orgs/twitter/repos?per_page=200 | ruby -rubygems -e 'require "json"; JSON.load(STDIN.read).each { |repo| %x[git clone #{repo["ssh_url"]} ]}'
Set author localy (config is for local folder only)
git config user.email [email protected]
git config user.name 'kajakas'
git commit --amend --reset-author
To change author in all commits, you can rebase to first commit
# find first commit <sha1-of-root>
git rebase -i `git log --reverse | if read a commit ; then echo $commit ; fi`
# replace all `pick` with `e`, edit = use commit, but stop for amending
# repeat the following command
git commit --amend --reset-author --no-edit && git rebase --continue
To change on initial commit you can use rebase --onto HEAD
# git checkout <sha1-of-root>
git checkout `git log --reverse | if read a commit ; then echo $commit ; fi`
git commit --amend --reset-author --no-edit
git rebase --onto HEAD HEAD master
Apply patch
git diff > patchfile
patch -p1 < patchfile
Pick only changes from certain files instead of cherry pick, exclude with
:^my-filder
or :^my-file
git show ffbd16fa313825e52b79ed9348b2a8ab963ec87a -- app/ :^spec | git apply -
Count files in staging
git diff --cached --numstat | wc -l
Rebase branches
git pull --rebase # pull remote changes and rebase your local commits
git rebase master # call this while you are on some feature branch
Interactivelly rebase pull request
git rebase -i HEAD~2 # rebase last two commits, or use origin/master
# pick : use same commit
# squash : meld with previous commit (on this list is previous)
# reword : change commit message
# edit : use commit but stop for amending
git fetch rails
git checkout guides_i18n
git rebase -i rails/master
git push origin guides_i18n --force-with-lease
To move commit to stage (it is similar to amend, but you have commit files on stage so you can restore –staged changes to unstage them)
git reset --soft HEAD^
Mark commit with a tag
git tag -l
git tag v1.5
# push tag is not automatically, you should do it manually
git push origin v1.5
# or push all tags
git push origin --tags
To find if given commit hash is incuded on some release (release is a tag)
git tag --contains <commit>
To remove tag
git tag -d v1.5
Clone for local or remote folders
git remote add local [email protected]:/home/orlovic/ruby/securiPi/.git
Big files
sudo apt install git-lfs
git lfs install
git lfs track *.mov
Bin folder added to path PATH=".git/safe/../../bin:$PATH"
, but only for
project where you run
mkdir -p .git/safe
Submodule is used when you have other git repository within your project. Add
submodule with git submodule add [email protected]:duleorlovic/myapp.git
When someone clone your project, it needs to do two steps: init and fetch.
Initialize configuration file with git submodule init
it actually creates
# .git/config
[submodule "myapp"]
active = true
url = [email protected]:duleorlovic/myapp.git
To fetch use git submodule update
to get actuall SHA that was commited.
To do both init and submodule update you can use option while cloning parent
(recursive mean to fetch submdules of submodules - nested submodules if exists)
git clone --recurse-submodules [email protected]:trkin/web-tips.git
or if it already cloned, it can pull latest parent changes and update
git pull
git submodule update --init --recursive
# or you can do in one step
git pull --recurse-submodules
This is needed every time you need to sync latest commited changes on parent.
On other side, to fetch upstream changes of submodules, you can navigate to each
submodule folder and checkout branch git chechout main
, get fetch
and git
merge
, or you can do from main repo using
git submodule update --remote
git submodule update --remote myapp # update only this submodule
git submodule update --remote --rebase myapp # stay on branch, not detached
To see differences in submodules you can use git diff --submodule
(or you can
set option git config --global diff.submodule log
so you do not need to pass
--submodule
attribute every time). Similar for status, you can se git config
status.submodulesummary 1
so git status
will show changes that are done for
submodules.
To see actual submodule commit which is commited in main repo
git log -p my-app
To work inside submodules you need to checkout branch (initially git submodule
update
is leaving the subrepository in detached HEAD state). After you commit
you need to push to submodule repo or you can do from superrepository with
git push --recurse-submodules=check
git push --recurse-submodules=on-demand
When some submodule become private, you can remove that
git rm apps/shoppe
Tutorials
git help tutorial-2
git help everyday
git help workflows
Use pre commit hooks to check for something before commit happens, for example
do not commit secret keys instead of DO_NOT_COMMIT
tag. You can
git commit --no-verify
to ignore hooks.
# .git/hooks/pre-commit
#!/bin/sh
# Redirect output to stderr.
exec 1>&2
# enable user input
exec < /dev/tty
grep_reg='-.*DO_NOT_COMMIT'
# CHECK
if test $(git diff --cached | grep -e $grep_reg | wc -l) != 0
then
exec git diff --cached | grep -ne $grep_reg
read -p "There are some occurrences of DO_NOT_COMMIT at your modification. Are you sure want to continue? (y/n)" yn
echo $yn | grep ^[Yy]$
if [ $? -eq 0 ]
then
exit 0; #THE USER WANTS TO CONTINUE
else
exit 1; # THE USER DONT WANT TO CONTINUE SO ROLLBACK
fi
fi
You can use .pre-commit-config.yaml
file
Another example is
#!/bin/bash
#
# https://gist.github.com/jimschubert/9073276
# Automatically adds branch name to every commit message.
# Modified from the stackoverflow answer here: http://stackoverflow.com/a/11524807/151445
#
# Get branch name, use sed to strip name if needed
# branchName=$(git branch | grep '*' | sed 's/* i//')
branchName=$(git branch --show-current)
# Get the first line, ex: from ammending or mmm from git commit -am'mmm'
firstLine=$(head -n1 $1)
if ! [[ $firstLine =~ $branchName ]] ; then
# Prepend branchName to COMMIT_MSG
echo "$branchName"' '$(cat "$1"| sed '/^#.*/d') > "$1"
#
# Append branchName to COMMIT_MSG
# echo -n "vishwasb/gfd-ui#$branchName" >> "$1" # this will add at the end
# echo $(cat "$1" | sed '/^#.*/d')' '"vishwasb/gfd-ui#$branchName" > "$1"
# Insert branchName at the end of the commit message file
# sed -i "1s@^@\n\n$branchName@" $1
fi
Add global gitignore for all projects
git config --global core.excludesfile ~/.gitignore
this will add to ~/.gitconfig
file. You could also use local .git/config
.
Here is my global config
# ~/.gitconfig
[alias]
# Git fetch pull all brancher and fast forward merge `git pull-all`
pull-all = !"for b in $(git for-each-ref refs/heads --format='%(refname)') ; do git checkout ${b#refs/heads/} ; git pull --ff-only ; done"
Github
For you project you can set instructions for issues, look example https://github.com/arsduo/koala/issues/new
Very usefull github cheat sheet https://github.com/tiimgreen/github-cheat-sheet#github-talks
Use labels:
devops
for CI tasksmissing_requirements
andready
for status of issuefeature
,bug
for type of issue
Use gitleaks to search history for secret keys, so you can remove them.
Hub
https://hub.github.com/
Download release for linux arm 64 https://github.com/github/hub/releases and
run sudo ./install
. To create alias, run and copy output hub alias -s
Hub add new commands:
git browse
open current project github page in the default browsergit browse -- issues
to open issues.git browse user/repo
to open user/repogit create
create this repository on GitHub and add GitHub as origin. After you create, you need to push- ci-status Show the CI status of a commit
- compare Open a compare page on GitHub
- fork Make a fork of a remote repository on GitHub and add as remote
- issue List or create issues
- pr Work with pull requests
- pull-request Open a pull request on GitHub
- release List or create releases
Github CLI
https://github.com/cli/cli
gh cli is used to automatically close pr
gh auth login
Check that you can connect using gh cli
gh repo clone some-private-repo
gh auth status
Permission problem is when you install gh cli using snap. Solved with installing with apt-get.
Prompt with branch name
# .bashrc
parse_git_branch() {
git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/ (\1)/'
}
# simplest is to attach branch name
# export PS1="$PS1\$(parse_git_branch)>"
# echo '$PS1'
# \[\e]0;\u@\h: \w\a\]${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$
export PS1="\[\e]0;\u@\h: \w\a\]${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\$(parse_git_branch)\[\033[00m\]\$ "