Skip to content

Instantly share code, notes, and snippets.

@mbbx6spp
Last active April 29, 2023 02:17
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mbbx6spp/2760f417eb8ab7ade11c7cb1f2cd6b43 to your computer and use it in GitHub Desktop.
Save mbbx6spp/2760f417eb8ab7ade11c7cb1f2cd6b43 to your computer and use it in GitHub Desktop.
Git tips and tricks for dev group presentation

Git Tips

Config

Git has different levels of configuration that apply to different /”scopes”/:

  • system (almost never needed; not covered here)
  • global (which is /”global for a user”/ scoped)
  • local (which is specific to one local clone)
  • worktree (which only applies to the current worktree; only relevant if you work with worktree s)
  • file (not covered here but you can set a git configuration option, when relevant at the file level, to one file)

The precendence of these configuration scopes is as follows (highest precedence first):

  1. file
  2. worktree
  3. local
  4. global
  5. system

As an example, if I have a local (/”clone”/ scoped) configuration option where foo.bar is set to false but my global (/”user”/ scoped) configuration option is set to true then when I run git config --get foo.bar in that overrided local clone repository, I will get back false.

# preconditions/setup
cd ~/
git config --global --add foo.bar true
git config --get foo.bar true
mkdir -p ~/tmp/test-repo
cd ~/tmp/test-repo
git init
git config --local --add foo.bar false
git config --get foo.bar # should show 'false'
git config --get-all foo.bar # should show 'true' followed by 'false' on a new line; showing precedence in reverse order
cd ~/
git config --get-all foo.bar # should show 'true'

Managing configuration

You can set a new value of the configuration like the following examples.

Set committer and author name for commits made across user’s repositories locally

git config --global --add user.name "Susan Potter"

Automatically setup tracking to the remote branch whenever you create branch from it

git config --global --add branch.autosetupmerge always 

Automatically setup local branches to always rebase on git-pull from tracking branch

git config --global --add branch.autosetuprebase always

You can get the value of a configuration option like so:

git config --global --get branch.autosetuprebase

List all your configuration options including overrides:

git config --list

--global config

  • Your user’s --global config can be found at $HOME/.gitconfig.
  • It looks something like this:
[user]
  name = "Your name"
  email = "your@email.tld"
[color]
  diff = auto
  status = auto
[apply]
  whitespace = strip
[pager]
  color = true
[alias]
  lsbr = branch -vv
# the rest of your ~/.gitconfig

--local config

  • Configuration that only applies to the current local clone of the repository
  • Find configuration inside the $(git root)/.git/config file
  • Override configuration for the local clone/repository scope with: git config --local --add foo.bar true

--worktree config

  • Only if you are inside a worktree
  • Find configuration inside the $(git root)/.git/worktree/<name>/config (I think)

Recommended Git Configuration

Configuration Value Reason
branch.autosetupmerge always local branches automatically track remote branch they were created off of
branch.autoseetuprebase always local branches will automatically rebase on top of the tracked branch on git pull, i.e. it expands to git pull --rebase ${remote} ${branch}
push.default nothing when running git push do not push any default branch to any default remote; nothing is assumed. Also consider current or upstream.
rebase.autoStash true stashes uncommitted changes to a local stash to rebase
user.name You name at work sometimes we go by different names
user.email Your work email address Gitlab has options to check committer and author emails upon receive; git defaults to ${USER}@${MACHINE_NAME} which is probably not what you want.
rerere.enabled true enables rerere as I suggested in #dev after Grant’s presentation two Friday’s ago when he talked about fearing rebasing.

Stashing

  • When you don’t want to commit yet because you are still working code changes out, but want to remember where you were at later.
  • Stash uncommitted changes like so: git stash
  • To pull back the last stash off the stash stack on top of the current branch: git stash apply
  • Means you can stash local only tool configurations and apply to different branches.

Relative References

  • HEAD^ - 1 commit before head
  • HEAD^^ 2 commits before head
  • HEAD~5 5 commits before head

Reflogs

git reflog directories can be found at .git/logs/refs/heads/, .git/logs/HEAD, and also .git/logs/refs/stash if the git stash has been used on the repo.

DEMO: CLI

Branches

  • git checkout - switches branch to prior branch
  • git fetch remote branch && git rebase -i remote/branch - fetch latest from remote for branch branch and then rebase your local branch on top of those changes from remote/branch you just fetched
  • git branch -vv - snazzier output on the CLI

if you want to work jointly on a branch:

  • git pull --rebase remote branch
  • git push remote branch --force-with-lease ensures you have at least pulled down pair’s contributions locally (but need to rebase too)

— END OF FIRST SESSION on 06/03/2020

— STARTING OF NEXT SESSION 06/24/2020

Merge base

Looking at common commits between branches: git merge-base HEAD origin/development

Tells us the commit SHA that we forked from. Check against the current HEAD of the remote branch after a git-fetch: git fetch origin/development && git rev-parse HEAD.

If these are the same, you are up to date.

However, you can look at the output of git branch -vv for more human readable indicators too.

Diffing

Demo:

  • git diff --stat provides diff stats
  • git diff --summary provides summary of diff
  • git diff --word-diff shows word diff

Git log

  • --oneline option gives a oneline log view (compact)
  • --graph a more whitespacey version of the tree view offered by tig
  • git whatchanged --since“2 days ago”= with iterations like:
    • git whatchanged --since“10 days ago” –oneline –stat= and also with --summary

Aliasing

Simple aliases can be added on the command line like this:

git config --global alias.unstage 'reset HEAD'

Aliases where you want to run multiple commands back to back including non-git commands will require you edit your ~/.gitconfig file under the [alias] section and have the entries look like the following:

[alias]
  # another simple examples
  co = checkout
  ci = commit
  # you can call aliases within aliases, see the "co" below
  freshbr = !git fetch origin && git co -b # you would run `git freshbr new-branch presistent-branch`

Custom subcommands

You can create a custom git subcommand by creating a script in your favorite scripting language. You will need to make the script executable and then add it to a directory in your $PATH.

For example I might add the following script in bash to my $HOME/bin folder which might look like the following in a file named git-greetings:

#!/usr/bin/env bash

main() {
  echo "Subcommand: ${0:-}"
  echo "Argument: ${1:-}"
  echo "All arguments: $*"
}

set -euo pipefail
case "${1:-}" in
  hello)
    main "hello"
    ;;
  bye)
    main "bye"
    ;;
  *)
    >&2 echo "Error: invalid command: ${1:-}"
    exit 1
    ;;
esac

Running git greetings hello will output the following to stdout stream with an exit code of 0:

Subcommand: /Users/sp721/bin/git-greetings
Argument: hello
All arguments: hello

Running git greetings foobar will output the following to stderr stream with an exit code of 1:

Error: invalid command: foobar

Prune

git remote prune origin

Worktree

git worktree add PATH COMMITISH
git worktree add -b new-branch-name PATH COMMITISH
git worktree remote PATH
git worktree prune
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment