[go: up one dir, main page]

Today I Learned

tags


2021/07/31

That you can use git grep to search for a string in a git repo.


2021/12/01

that you can write any file in your git history using git show <ref>:<path>.

See https://juplo.de/cat-any-file-in-any-commit-with-git/


2022/03/06

About git-sparse-checkout:

The general script for doing a sparse checkout is:

#!/usr/bin/env bash
git_url="${git_url:?required}"
target_dir="${target_dir:?required}"

is_git_dir() { git rev-parse; }

init() {
  mkdir -p "$target_dir" && cd "$target_dir"
  if ! is_git_dir; then
    git init && git remote add -f origin "$git_url"
  fi

  git config core.sparseCheckout true &&
    git sparse-checkout init &&
    git sparse-checkout set test &&
    git pull origin master
}

see also: https://about.gitlab.com/blog/2020/03/13/partial-clone-for-massive-repositories/


2022/08/06

How to consistently establish relative symlinks within a git repo:

cd "$target_dir" # the directory in which you want to create the symlink
ln -s ../relative/path/to/source.file ./symlink.name

https://stackoverflow.com/a/15465514/6571327


2023/02/01

git restore exists, and works like a more destructive git stash

See https://git-scm.com/docs/git-restore


2023/03/02

that there’s a git commit --trailer <token>:<value> flag.

https://git-scm.com/docs/git-commit#Documentation/git-commit.txt---trailerlttokengtltvaluegt


2023/03/09

This was a bit of a rollercoaster morning: (1) I tried git worktree add $branch, and it was pretty convenient! (2) until I got a pre-commit error, then I got error: unable to read $HASH on git status (3) which led me to discover git fsck --full --name-objects, which is currently yelling about error: $HASH: invalid sha1 pointer in cache-tree


2023/05/16

That there are instances where a git commit-message can start with a #:

set -eu
experiment="$(mkdtemp -d)"
cd "$experiment"
git init#
touch readme && git add readme
git commit -m "# not a comment, apparently"
actual="$(cat ./.git/COMMIT_EDITMSG)"
expected="# not a comment, apparently"
test "$actual" = "$expected"
git log
# <hash>
# # not a comment, apparently

2023/07/30

Getting the top-level directory of the relevant git worktree

#!/usr/bin/env bash
# set up a dummy repo
set -euo pipefail
experiment_dir="$(mktemp -d --tmpdir test_repo.XXX)"
git clone --recurse-submodules git@github.com:SKalt/dummy_repo.git "$experiment_dir"
cd "$experiment_dir"
git pull --recurse-submodules=yes

git checkout -b brnch && git checkout - # set up a dummy branch
git worktree add wrktr brnch # add a wortree
cd wrktr &&  git submodule update --init --recursive # init the recursive submodules in the worktree
tree .
.
├── LICENSE
├── nested
│   └── file.txt
├── README.md
├── submod
│   ├── LICENSE
│   ├── nested
│   │   └── file.txt
│   ├── README.md
│   └── submod
│       ├── LICENSE
│       ├── nested
│       │   └── file.txt
│       └── README.md
└── wrktr
    ├── LICENSE
    ├── nested
    │   └── file.txt
    ├── README.md
    └── submod
        ├── LICENSE
        ├── nested
        │   └── file.txt
        ├── README.md
        └── submod
            ├── LICENSE
            ├── nested
            │   └── file.txt
            └── README.md

11 directories, 18 files
tree .git
./.git
├── branches
├── config
├── description
├── HEAD
├── hooks/...
├── index
├── info/...
├── logs/...
├── modules
│   └── submod
│       ├── branches
│       ├── config
│       ├── description
│       ├── HEAD
│       ├── hooks/...
│       ├── index
│       ├── info/...
│       ├── logs/...
│       ├── modules
│       │   └── submod
│       │       ├── branches
│       │       ├── config # <- contains relative path in core.worktree
│       │       ├── description
│       │       ├── HEAD
│       │       ├── hooks/...
│       │       ├── index
│       │       ├── info/...
│       │       ├── logs/...
│       │       ├── objects/...
│       │       ├── packed-refs
│       │       └── refs/...
│       ├── objects/...
│       ├── packed-refs
│       └── refs/...
├── objects/...
├── packed-refs
├── refs
│   ├── heads
│   │   ├── brnch
│   │   └── main
│   ├── remotes
│   │   └── origin
│   │       └── HEAD
│   └── tags
└── worktrees
    └── wrktr
        ├── commondir
        ├── gitdir
        ├── HEAD
        ├── index
        ├── logs
        │   └── HEAD
        ├── modules
        │   └── submod
        │       ├── branches
        │       ├── config
        │       ├── description
        │       ├── HEAD
        │       ├── hooks/...
        │       ├── index
        │       ├── info/...
        │       ├── logs/...
        │       ├── modules
        │       │   └── submod
        │       │       ├── branches
        │       │       ├── config
        │       │       ├── description
        │       │       ├── HEAD
        │       │       ├── hooks/...
        │       │       ├── index
        │       │       ├── info/...
        │       │       ├── logs/...
        │       │       ├── objects/...
        │       │       ├── packed-refs
        │       │       └── refs/...
        │       ├── objects/...
        │       ├── packed-refs
        │       └── refs/...
        └── ORIG_HEAD

91 directories, 133 files
args=(
    --git-common-dir
    --git-dir
    --is-inside-git-dir
    --is-inside-work-tree
    --show-toplevel
);

padding() {
    local delimiter="$1" # must be 1ch
    local message="$2"
    local width="${3:-78}"
    printf -- "${delimiter}%.0s" $(seq 0 $(($width - ${#message})))
}

header() {
    message="$(printf "%s " "$1")"
    echo
    printf "%s" "$message"; padding "#" "$message"; echo
    echo
}
indent() {
    padding " " "$1" 22;
    printf "%s = " "$1";
}

explore() {
    local to_explore="$1"
    {
        cd "$to_explore"
        header "$to_explore"
        for arg in "${args[@]}"; do
            indent "$arg";
            git --no-pager rev-parse "$arg" 2>&1 || true;
        done

        indent "config#core.worktree"
        git --no-pager config core.worktree || echo "<missing>"

        indent "./config#core.worktree" 22
        if [ -f ./config ]; then
            git --no-pager config --file ${PWD}/config core.worktree || echo "<missing>"
        else
            echo
        fi
        indent "./gitdir"
        if [ -f ./gitdir ]; then
            cat ./gitdir;
        else
            echo
        fi
    } | sed 's/^/# /g' | sed "s#${PWD}#\${PWD}#g"
}

paths_to_explore=(
    "${PWD}"
    "${PWD}/nested"
    "${PWD}/.git"
    "${PWD}/.git/worktrees"
    "${PWD}/.git/worktrees/wrktr"
    "${PWD}/.git/worktrees/wrktr/modules/submod"
    "${PWD}/wrktr"
    "${PWD}/wrktr/submod"
    "${PWD}/submod"
    "${PWD}/.git/modules/submod"
)

for p in "${paths_to_explore[@]}"; do explore "$p"; done
#
# ${PWD} ############################################################
#
#        --git-common-dir = .git
#               --git-dir = .git
#     --is-inside-git-dir = false
#   --is-inside-work-tree = true
#         --show-toplevel = ${PWD}
#    config#core.worktree = <missing>
#  ./config#core.worktree =
#                ./gitdir =
#
# ${PWD}/nested #####################################################
#
#        --git-common-dir = ../.git
#               --git-dir = ${PWD}/.git
#     --is-inside-git-dir = false
#   --is-inside-work-tree = true
#         --show-toplevel = ${PWD}
#    config#core.worktree = <missing>
#  ./config#core.worktree =
#                ./gitdir =
#
# ${PWD}/.git #######################################################
#
#        --git-common-dir = .
#               --git-dir = .
#     --is-inside-git-dir = true
#   --is-inside-work-tree = false
#         --show-toplevel = fatal: this operation must be run in a work tree
#    config#core.worktree = <missing>
#  ./config#core.worktree = <missing>
#                ./gitdir =
#
# ${PWD}/.git/worktrees #############################################
#
#        --git-common-dir = ${PWD}/.git
#               --git-dir = ${PWD}/.git
#     --is-inside-git-dir = true
#   --is-inside-work-tree = false
#         --show-toplevel = fatal: this operation must be run in a work tree
#    config#core.worktree = <missing>
#  ./config#core.worktree =
#                ./gitdir =
#
# ${PWD}/.git/worktrees/wrktr #######################################
#
#        --git-common-dir = ${PWD}/.git
#               --git-dir = .
#     --is-inside-git-dir = true
#   --is-inside-work-tree = false
#         --show-toplevel = fatal: this operation must be run in a work tree
#    config#core.worktree = <missing>
#  ./config#core.worktree =
#                ./gitdir = ${PWD}/wrktr/.git
#
# ${PWD}/.git/worktrees/wrktr/modules/submod ########################
#
#        --git-common-dir = .
#               --git-dir = .
#     --is-inside-git-dir = false
#   --is-inside-work-tree = false
#         --show-toplevel = ${PWD}/wrktr/submod
#    config#core.worktree = ../../../../../wrktr/submod
#  ./config#core.worktree = ../../../../../wrktr/submod
#                ./gitdir =
#
# ${PWD}/wrktr ######################################################
#
#        --git-common-dir = ${PWD}/.git
#               --git-dir = ${PWD}/.git/worktrees/wrktr
#     --is-inside-git-dir = false
#   --is-inside-work-tree = true
#         --show-toplevel = ${PWD}/wrktr
#    config#core.worktree = <missing>
#  ./config#core.worktree =
#                ./gitdir =
#
# ${PWD}/wrktr/submod ###############################################
#
#        --git-common-dir = ${PWD}/.git/worktrees/wrktr/modules/submod
#               --git-dir = ${PWD}/.git/worktrees/wrktr/modules/submod
#     --is-inside-git-dir = false
#   --is-inside-work-tree = true
#         --show-toplevel = ${PWD}/wrktr/submod
#    config#core.worktree = ../../../../../wrktr/submod
#  ./config#core.worktree =
#                ./gitdir =
#
# ${PWD}/submod #####################################################
#
#        --git-common-dir = ${PWD}/.git/modules/submod
#               --git-dir = ${PWD}/.git/modules/submod
#     --is-inside-git-dir = false
#   --is-inside-work-tree = true
#         --show-toplevel = ${PWD}/submod
#    config#core.worktree = ../../../submod
#  ./config#core.worktree =
#                ./gitdir =
#
# ${PWD}/.git/modules/submod ########################################
#
#        --git-common-dir = .
#               --git-dir = .
#     --is-inside-git-dir = false
#   --is-inside-work-tree = false
#         --show-toplevel = ${PWD}/submod
#    config#core.worktree = ../../../submod
#  ./config#core.worktree = ../../../submod
#                ./gitdir =
explore_tab() {
    local to_explore="$1"
    {
        cd "$to_explore"

        printf '|`%s`' "$to_explore"
        for arg in "${args[@]}"; do
            _result="$(git --no-pager rev-parse "$arg" 2>&1 || true)"
            printf '|`%s`' "$_result"
        done
        # config#core.worktree
        printf '|`%s`' "$(git --no-pager config core.worktree || echo "<missing>")"

        # ./config#core.worktree
        printf '|'
        if [ -f ./config ]; then
            printf '`%s`' "$(
                git --no-pager config --file ${PWD}/config core.worktree || echo "<missing>"
            )"
        fi
        # ./gitdir
        printf "|"
        if [ -f ./gitdir ]; then
            printf '`%s`' "$(cat ./gitdir)";
        fi
        echo "|"
    }
}
_headers=(
    "pwd"
    "${args[@]}"
    "config#core.worktree"
    ./config#core.worktree
    ./gitdir
)
for i in "${_headers[@]}"; do printf '|`%s`' "$i"; done
echo "|"
for i in "${_headers[@]}"; do printf "|-"; done
echo "|"
for p in "${paths_to_explore[@]}"; do
  explore_tab "$p" | sed "s#${PWD}#\${PWD}#g";
done
pwd--git-common-dir--git-dir--is-inside-git-dir--is-inside-work-tree--show-toplevelconfig#core.worktree./config#core.worktree./gitdir
${PWD}.git.gitfalsetrue${PWD}<missing>
${PWD}/nested../.git${PWD}/.gitfalsetrue${PWD}<missing>
${PWD}/.git..truefalsefatal: this operation must be run in a work tree<missing><missing>
${PWD}/.git/worktrees${PWD}/.git${PWD}/.gittruefalsefatal: this operation must be run in a work tree<missing>
${PWD}/.git/worktrees/wrktr${PWD}/.git.truefalsefatal: this operation must be run in a work tree<missing>${PWD}/wrktr/.git
${PWD}/.git/worktrees/wrktr/modules/submod..falsefalse${PWD}/wrktr/submod../../../../../wrktr/submod../../../../../wrktr/submod
${PWD}/wrktr${PWD}/.git${PWD}/.git/worktrees/wrktrfalsetrue${PWD}/wrktr<missing>
${PWD}/wrktr/submod${PWD}/.git/worktrees/wrktr/modules/submod${PWD}/.git/worktrees/wrktr/modules/submodfalsetrue${PWD}/wrktr/submod../../../../../wrktr/submod
${PWD}/submod${PWD}/.git/modules/submod${PWD}/.git/modules/submodfalsetrue${PWD}/submod../../../submod
${PWD}/.git/modules/submod..falsefalse${PWD}/submod../../../submod../../../submod
pwd--git-common-dir--git-dir--is-inside-git-dir--is-inside-work-tree--show-toplevelconfig#core.worktree./config#core.worktree./gitdir
${PWD}/.git..truefalsefatal: this operation must be run in a work tree<missing><missing>
${PWD}/.git/worktrees${PWD}/.git${PWD}/.gittruefalsefatal: this operation must be run in a work tree<missing>
${PWD}/.git/worktrees/wrktr${PWD}/.git.truefalsefatal: this operation must be run in a work tree<missing>${PWD}/wrktr/.git
${PWD}/.git/worktrees/wrktr/modules/submod..falsefalse${PWD}/wrktr/submod../../../../../wrktr/submod../../../../../wrktr/submod
${PWD}/.git/modules/submod..falsefalse${PWD}/submod../../../submod../../../submod

2024/04/20

That git will place a file with the (absolute?) path to the actual git dir if initialized with git init --separate-git-dir. This is intended as an FS-agnostic symlink.

The lesson here is to never assume repo/.git exists and is a directory; use

git rev-parse --absolute-git-dir

docs


2024/06/17

That you can use the .gitmodules document in the parent repo to configure a submodule to track a branch with the same name as the parent repo’s current branch:

submodule..branch

[…] A special value of . is used to indicate that the name of the branch in the submodule should be the same name as the current branch in the current repository.

https://git-scm.com/docs/gitmodules#Documentation/gitmodules.txt-submoduleltnamegtbranch


2025/01/06

That you can configure git to use an alternative git directory using git config core.hooksPath as of git 2.9.


2025/01/07

That a 0-hour contract is a UK labor setup where the employer doesn’t guaruntee any work hours to the employee, but binds them with a contract. https://en.wikipedia.org/wiki/Zero-hour_contract


That you can configure git to append a Signed-off-by: ... trailer using git config format.signOff true.

That you can configure git to use your SSH key to sign commits and tags with

git config gpg.format ssh
git config commit.gpgSign true
git config tag.gpgSign true

See


2025/01/08

That zsh completions in /usr/share/zsh/vendor-completions must start with a _ in order to work, e.g. /usr/share/zsh/vendor-completions/_rg. Not sure why, though.


While you can use the same SSH key for both authentication and signing, NIST recommends you shouldn’t: https://gist.github.com/ChristopherA/3d6a2f39c4b623a1a287b3fb7e0aa05b

git config commit.gpgSign true
git config tag.gpgSign true
git config gpg.format ssh
git config user.signingKey 'ssh-ed25519 AAAA...xyz'

2025/02/07

That to verify git SSH commit signatures locally, you need to

git config gpg.ssh.allowedSignersFile $PATH_TO_ALLOWED_SIGNERS_FILE

The signers file will be in the form

user@email.com namespace="git" ssh-ed25519 AAA...jhq user@email.com

Once that’s set up. you can run

git log --show-signature

To verify signatures locally. Alternately, you can run

git log --format="signed:%G? fingerprint:%GF key:%GK"

See https://git-scm.com/docs/git-log#Documentation/git-log.txt-emGGem for more signature-related log format directives.


2025/10/12

That git ships a feature that remembers how you resolve conflicts, git-rerere: https://git-scm.com/book/en/v2/Git-Tools-Rerere.