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:

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 tasks
  • missing_requirements and ready for status of issue
  • feature, 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 browser git browse -- issues to open issues. git browse user/repo to open user/repo
  • git 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\]\$ "