From 464d891255f8ede89a5d35c5067dbd2e6c059ee5 Mon Sep 17 00:00:00 2001 From: John Freeman Date: Sun, 17 Sep 2023 12:21:13 +0100 Subject: [PATCH] Improved Git config * New utility aliases (e.g. to cope with `main`/`master` branch difference) * Prefer `switch` over `checkout` * New aliases for working with remote `HEAD` * Configure mergetools and difftools * Other Git config improvements --- .editorconfig | 6 +- docs/_docs/features.md | 60 +++++-- provisioning/playbook.yml | 151 +++++++++++++++--- provisioning/requirements.yml | 2 - .../roles/git_config/defaults/main.yml | 2 + provisioning/roles/git_config/tasks/main.yml | 9 ++ .../git_helpers/files/git-develop-branch.sh | 10 ++ .../git_helpers/files/git-main-branch.sh | 10 ++ provisioning/roles/git_helpers/tasks/main.yml | 11 ++ .../roles/more_cli_tools/tasks/main.yml | 1 + provisioning/roles/zsh/files/custom.zsh | 21 --- 11 files changed, 218 insertions(+), 65 deletions(-) create mode 100644 provisioning/roles/git_config/defaults/main.yml create mode 100644 provisioning/roles/git_config/tasks/main.yml create mode 100644 provisioning/roles/git_helpers/files/git-develop-branch.sh create mode 100644 provisioning/roles/git_helpers/files/git-main-branch.sh create mode 100644 provisioning/roles/git_helpers/tasks/main.yml diff --git a/.editorconfig b/.editorconfig index e8139ca1..c26ae0e3 100644 --- a/.editorconfig +++ b/.editorconfig @@ -15,5 +15,9 @@ trim_trailing_whitespace = true indent_size = 2 # Shell scripts can be fussy about line endings -[*.sh] +[*.{sh,zsh}] end_of_line = lf + +# Match shfmt +[*.sh] +indent_style = tab diff --git a/docs/_docs/features.md b/docs/_docs/features.md index 04867399..c156d446 100644 --- a/docs/_docs/features.md +++ b/docs/_docs/features.md @@ -5,7 +5,7 @@ description: > Features provided by the GantSign EnV development environment. numbered_headings: yes date: 2017-01-18T16:35:52+00:00 -modified: 2023-09-01T21:38:26+01:00 +modified: 2023-09-17T12:02:25+01:00 --- There are a lot of well known projects, and hidden gems, which aid in your @@ -370,29 +370,49 @@ project. Git aliases save you a lot of typing and make it easy to execute more complex Git commands: +* Print the main branch (searches for `main`, `trunk`, `mainline`, `default` and `master`) + + `git main` = `git-main-branch` (shell script) + +* Print the develop branch (searches for `dev`, `devel`, `development` and `develop`) + + `git dev` = `git-develop-branch` (shell script) + +* Print the remote` HEAD` branch + + `git rhead` = `git remote show origin | grep -Po 'HEAD branch: \K.*'` + +* Print the repository root directory + + `git root` = `git rev-parse --show-toplevel` + +* Execute a command in the repository root + + `git exec` = `f() { exec "$@"; }; f` (shell function) + * View summary lines for recent commits - `git ls` ≡ `git log --pretty=format:"%C(yellow)%h%Cred%d\\ %Creset%s%Cblue\\ [%cn]" --decorate` + `git ls` ≡ `git log --pretty=format:"%C(yellow)%h%Cred%d\ %Creset%s%Cblue\ [%an]" --decorate` * View summary lines and list changed files for recent commits - `git ll` ≡ `git log --pretty=format:"%C(yellow)%h%Cred%d\\ %Creset%s%Cblue\\ [%cn]" --decorate --numstat` + `git ll` ≡ `git log --pretty=format:"%C(yellow)%h%Cred%d\ %Creset%s%Cblue\ [%an]" --decorate --numstat` * View summary lines and dates for recent commits - `git lds` ≡ `git log --pretty=format:"%C(yellow)%h\\ %ad%Cred%d\\ %Creset%s%Cblue\\ [%cn]" --decorate --date=short` + `git lds` ≡ `git log --pretty=format:"%C(yellow)%h\ %ad%Cred%d\ %Creset%s%Cblue\ [%an]" --decorate --date=short` * View tree of recent commits (all branches) `git lt` ≡ `git log --oneline --graph --decorate --all` -* Checkout an existing branch +* Switch to an existing branch - `git co` ≡ `git checkout` + `git sw` ≡ `git switch` * Create a new branch - `git cb` ≡ `git checkout -b` + `git cb` ≡ `git switch -c` * Amend the last commit and change the commit message @@ -402,13 +422,17 @@ Git commands: `git cane` ≡ `git commit --amend --no-edit` -* Rebase the current branch onto `origin/master` +* Rebase the current branch onto `origin/main` - `git rom` ≡ `git rebase origin/master` + `git rom` ≡ `git fetch -p && git rebase origin/$(git main)` * Rebase the current branch onto `origin/develop` - `git rod` ≡ `git rebase origin/develop` + `git rod` ≡ `git fetch -p && git rebase origin/$(git dev)` + +* Rebase the current branch onto `origin/HEAD` + + `git roh` ≡ `!git fetch -p && git rebase origin/$(git rhead)` * Push the current branch to `origin HEAD` @@ -418,13 +442,17 @@ Git commands: `git pof` ≡ `git push origin HEAD --force` -* Switch to the `master` branch pull changes and prune remote branches +* Switch to the `main` branch, pull changes and prune remote branches + + `git smp` ≡ `git switch $(git main) && git pull -p` + +* Switch to the `develop` branch, pull changes and prune remote branches - `git cmp` ≡ `git checkout master && git pull -p` + `git sdp` ≡ `git switch $(git dev) && git pull -p` -* Switch to the `develop` branch pull changes and prune remote branches +* Switch to the local branch with the same name as the remote head, pull changes and prune remote branches - `git cdp` ≡ `git checkout develop && git pull -p` + `git shp` ≡ `git fetch -p && git switch $(git rhead) && git pull -p` * Pop the most recent stash @@ -432,11 +460,11 @@ Git commands: * List the most recently checked-out branches - `git lb` ≡ `!git reflog show --pretty=format:'%gs ~ %gd' --date=relative | grep 'checkout:' | grep -oE '[^ ]+ ~ .*' | awk -F~ '!seen[$1]++' | head -n 10 | awk -F' ~ HEAD@{' '{printf(\" \\033[33m%s: \\033[37m %s\\033[0m\\n\", substr($2, 1, length($2)-1), $1)}'` + `git lb` ≡ `git reflog show --pretty=format:'%gs ~ %gd' --date=relative | grep 'checkout:' | grep -oE '[^ ]+ ~ .*' | awk -F~ '!seen[$1]++' | head -n 10 | awk -F' ~ HEAD@{' '{printf(" \033[33m%s: \033[37m %s\033[0m\n", substr($2, 1, length($2)-1), $1)}'` * Reformat the recent changes as Markdown release notes - `git release-notes` ≡ `git log --color --pretty=format:'%s%Cred%d%Creset' --decorate | sed -E 's/(.*) \\((\\#[0-9]+)\\)/* \\2: \\1/' | tac -` + `git release-notes` ≡ `git log --color --pretty=format:'%s%Cred%d%Creset' --decorate | sed -E 's/(.*) \((\#[0-9]+)\)/* \2: \1/' | tac -` ### Git-GUI diff --git a/provisioning/playbook.yml b/provisioning/playbook.yml index 7ba04b79..d4e9cb50 100644 --- a/provisioning/playbook.yml +++ b/provisioning/playbook.yml @@ -136,32 +136,133 @@ tags: - homebrew - # Install and configure Git version control - - role: weareinteractive.git + # Install Git helper scripts + - role: git_helpers + + # Configure Git version control + - role: git_config + become_user: '{{ my_user }}' + git_config: + core.autocrlf: input # Force Linux line endings + diff.algorithm: histogram # For improved diff + diff.indentHeuristic: 'true' # For improved diff + diff.mnemonicPrefix: 'true' # For more readable diff + difftool.prompt: 'false' # Suppress annoying prompt launching difftool + init.defaultBranch: main + log.date: 'format:%a %Y-%m-%d %H:%M:%S %z' # Use ISO format + mergetool.prompt: 'false' # Suppress annoying prompt launching mergetool + push.autoSetupRemote: 'true' # So you can pull the branch later + push.default: simple # Use same branch names for local and remote + rerere.autoUpdate: 'true' # Avoid having to redo a merge + rerere.enabled: 'true' # Avoid having to redo a merge + user.useConfigOnly: 'true' # Don't guess username / email + + # utility aliases + alias.dev: '!git-develop-branch' + alias.main: '!git-main-branch' + alias.rhead: >- + !git remote show origin | grep -Po 'HEAD branch: \K.*' + alias.root: 'rev-parse --show-toplevel' + alias.lb: >- + !git reflog show --pretty=format:'%gs ~ %gd' --date=relative + | grep 'checkout:' + | grep -oE '[^ ]+ ~ .*' + | awk -F~ '!seen[$1]++' + | head -n 10 + | awk -F' ~ HEAD@{' '{printf(" \033[33m%s: \033[37m %s\033[0m\n", substr($2, 1, length($2)-1), $1)}' + alias.exec: '!f() { exec "$@"; }; f' + # log output + alias.lds: 'log --pretty=format:"%C(yellow)%h\ %ad%Cred%d\ %Creset%s%Cblue\ [%an]" --decorate --date=short' + alias.ll: 'log --pretty=format:"%C(yellow)%h%Cred%d\ %Creset%s%Cblue\ [%an]" --decorate --numstat' + alias.ls: 'log --pretty=format:"%C(yellow)%h%Cred%d\ %Creset%s%Cblue\ [%an]" --decorate' + alias.lt: 'log --oneline --graph --decorate --all' + alias.release-notes: >- + !git log --color --pretty=format:'%s%Cred%d%Creset' --decorate | sed -E 's/(.*) \((\#[0-9]+)\)/* \2: \1/' | tac - + # switching branch + alias.sw: 'switch' + alias.cb: 'switch -c' + alias.sdp: '!git switch $(git dev) && git pull -p' + alias.shp: '!git fetch -p && git switch $(git rhead) && git pull -p' + alias.smp: '!git switch $(git main) && git pull -p' + # amending commits + alias.ca: 'commit --amend' + alias.cane: 'commit --amend --no-edit' + # pushing + alias.po: 'push origin HEAD' + alias.pof: 'push origin HEAD --force' + # rebasing + alias.rod: '!git fetch -p && git rebase origin/$(git dev)' + alias.roh: '!git fetch -p && git rebase origin/$(git rhead)' + alias.rom: '!git fetch -p && git rebase origin/$(git main)' + # other + alias.pop: 'stash pop' + + # Configure Git version control for Delta + - role: git_config + become_user: '{{ my_user }}' + git_config: + # Delta configuration + pager.diff: delta + pager.log: delta + pager.reflog: delta + pager.show: delta + delta.line-numbers: 'true' + # Adjust appearance to match bat theme + delta.line-numbers-left-style: '#619DD7' + delta.line-numbers-right-style: '#619DD7' + delta.file-style: white bold + delta.file-decoration-style: white ol + delta.hunk-header-line-number-style: '#619DD7' + delta.hunk-header-decoration-style: '#619DD7 ul' + # Improve contrast between background and foreground colors + delta.minus-style: 'syntax #6A1212' + delta.minus-non-emph-style: 'syntax #481818' + delta.minus-emph-style: 'syntax #6A1212' + delta.line-numbers-minus-style: '#8C1717' + delta.plus-style: 'syntax #4C5631' + delta.plus-non-emph-style: 'syntax #383D28' + delta.plus-emph-style: 'syntax #4C5631' + delta.line-numbers-plus-style: '#7C8C4F' + tags: + - homebrew + + # Configure Git version control for GIRT + - role: git_config + become_user: '{{ my_user }}' + git_config: + sequence.editor: interactive-rebase-tool + tags: + - homebrew + + # Configure Git version control for Meld + - role: git_config + become_user: '{{ my_user }}' git_config: - core: - autocrlf: input - alias: - ls: 'log --pretty=format:"%C(yellow)%h%Cred%d\\ %Creset%s%Cblue\\ [%cn]" --decorate' - ll: 'log --pretty=format:"%C(yellow)%h%Cred%d\\ %Creset%s%Cblue\\ [%cn]" --decorate --numstat' - lds: 'log --pretty=format:"%C(yellow)%h\\ %ad%Cred%d\\ %Creset%s%Cblue\\ [%cn]" --decorate --date=short' - lt: 'log --oneline --graph --decorate --all' - co: 'checkout' - cb: 'checkout -b' - ca: 'commit --amend' - cane: 'commit --amend --no-edit' - rom: 'rebase origin/master' - rod: 'rebase origin/develop' - po: 'push origin HEAD' - pof: 'push origin HEAD --force' - cmp: '!git checkout master && git pull -p' - cdp: '!git checkout develop && git pull -p' - pop: 'stash pop' - # git needs the following commands below to be quoted so we use > to preserve quotes - lb: > - "!git reflog show --pretty=format:'%gs ~ %gd' --date=relative | grep 'checkout:' | grep -oE '[^ ]+ ~ .*' | awk -F~ '!seen[$1]++' | head -n 10 | awk -F' ~ HEAD@{' '{printf(\" \\033[33m%s: \\033[37m %s\\033[0m\\n\", substr($2, 1, length($2)-1), $1)}'" - release-notes: > - "!git log --color --pretty=format:'%s%Cred%d%Creset' --decorate | sed -E 's/(.*) \\((\\#[0-9]+)\\)/* \\2: \\1/' | tac -" + difftool.meld.cmd: 'meld "$LOCAL" "$REMOTE"' + merge.tool: meld + mergetool.meld.cmd: 'meld "$LOCAL" "$MERGED" "$REMOTE"' + tags: + - gui + + # Configure Git version control for VS Code + - role: git_config + become_user: '{{ my_user }}' + git_config: + difftool.code.cmd: 'code --reuse-window --wait --diff "$LOCAL" "$REMOTE"' + mergetool.code.cmd: 'code --reuse-window --wait "$MERGED"' + tags: + - gui + - vscode + + # Configure Git version control for IntelliJ + - role: git_config + become_user: '{{ my_user }}' + git_config: + difftool.idea.cmd: 'idea diff "$LOCAL" "$REMOTE"' + mergetool.idea.cmd: 'idea merge "$LOCAL" "$REMOTE" "$BASE" "$MERGED"' + tags: + - gui + - intellij # Configure Git user name and email - role: gantsign.git_user diff --git a/provisioning/requirements.yml b/provisioning/requirements.yml index 0ba71d76..e617ae43 100644 --- a/provisioning/requirements.yml +++ b/provisioning/requirements.yml @@ -1,8 +1,6 @@ --- - src: geerlingguy.docker version: '6.2.0' -- src: weareinteractive.git - version: '1.3.3' - src: weareinteractive.environment version: '1.5.0' - src: geerlingguy.nodejs diff --git a/provisioning/roles/git_config/defaults/main.yml b/provisioning/roles/git_config/defaults/main.yml new file mode 100644 index 00000000..da92d60a --- /dev/null +++ b/provisioning/roles/git_config/defaults/main.yml @@ -0,0 +1,2 @@ +--- +git_config: {} diff --git a/provisioning/roles/git_config/tasks/main.yml b/provisioning/roles/git_config/tasks/main.yml new file mode 100644 index 00000000..265230e2 --- /dev/null +++ b/provisioning/roles/git_config/tasks/main.yml @@ -0,0 +1,9 @@ +--- +- name: Configure Git + community.general.git_config: + scope: global + name: '{{ item.key }}' + value: '{{ item.value }}' + loop_control: + label: "{{ item.key }}={{ item.value }}" + loop: '{{ git_config | dict2items }}' diff --git a/provisioning/roles/git_helpers/files/git-develop-branch.sh b/provisioning/roles/git_helpers/files/git-develop-branch.sh new file mode 100644 index 00000000..a21e1f6d --- /dev/null +++ b/provisioning/roles/git_helpers/files/git-develop-branch.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +command git rev-parse --git-dir &>/dev/null || exit +for branch in dev devel development; do + if command git show-ref -q --verify refs/heads/$branch; then + echo $branch + exit + fi +done +echo develop diff --git a/provisioning/roles/git_helpers/files/git-main-branch.sh b/provisioning/roles/git_helpers/files/git-main-branch.sh new file mode 100644 index 00000000..62acb46f --- /dev/null +++ b/provisioning/roles/git_helpers/files/git-main-branch.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +command git rev-parse --git-dir &>/dev/null || exit +for ref in refs/{heads,remotes/{origin,upstream}}/{main,trunk,mainline,default}; do + if command git show-ref -q --verify $ref; then + echo ${ref##*/} + exit + fi +done +echo master diff --git a/provisioning/roles/git_helpers/tasks/main.yml b/provisioning/roles/git_helpers/tasks/main.yml new file mode 100644 index 00000000..ca8d9b8f --- /dev/null +++ b/provisioning/roles/git_helpers/tasks/main.yml @@ -0,0 +1,11 @@ +--- +- name: Copy Git helper scripts + ansible.builtin.copy: + src: '{{ item }}.sh' + dest: '/usr/local/bin/{{ item }}' + mode: 'u=rwx,go=rx' + owner: root + group: root + loop: + - git-develop-branch + - git-main-branch diff --git a/provisioning/roles/more_cli_tools/tasks/main.yml b/provisioning/roles/more_cli_tools/tasks/main.yml index a186aeb5..2491b576 100644 --- a/provisioning/roles/more_cli_tools/tasks/main.yml +++ b/provisioning/roles/more_cli_tools/tasks/main.yml @@ -9,6 +9,7 @@ - dust - gh - git-delta + - git-interactive-rebase-tool - lsd - procs - sd diff --git a/provisioning/roles/zsh/files/custom.zsh b/provisioning/roles/zsh/files/custom.zsh index 84449bfd..b45fdaba 100644 --- a/provisioning/roles/zsh/files/custom.zsh +++ b/provisioning/roles/zsh/files/custom.zsh @@ -101,27 +101,6 @@ alias -s tar.zsd='tar -I zstd -tf' alias -s ace='unace l' alias -s 7z='7z l' -# Git branch helpers -if [ ! -f ~/.local/bin/git-main-branch ]; then - mkdir -p "$HOME/.local/bin" - echo "\ -#!/usr/bin/bash - -$(which git_main_branch) - -git_main_branch" > ~/.local/bin/git-main-branch - chmod 'u=rwx,go=r' ~/.local/bin/git-main-branch -fi -if [ ! -f ~/.local/bin/git-develop-branch ]; then - echo "\ -#!/usr/bin/bash - -$(which git_develop_branch) - -git_develop_branch" > ~/.local/bin/git-develop-branch - chmod 'u=rwx,go=r' ~/.local/bin/git-develop-branch -fi - # Git clone and cd git-clone-cd() { local arg