2021/07/31
That you can use git grep to search for a string in a git repo.
That you can use git grep to search for a string in a git repo.
that you can write any file in your git history using git show <ref>:<path>.
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/
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
git restore exists, and works like a more destructive git stash
that there’s a git commit --trailer <token>:<value> flag.
https://git-scm.com/docs/git-commit#Documentation/git-commit.txt---trailerlttokengtltvaluegt
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
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
#!/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-toplevel | config#core.worktree | ./config#core.worktree | ./gitdir |
|---|---|---|---|---|---|---|---|---|
${PWD} | .git | .git | false | true | ${PWD} | <missing> | ||
${PWD}/nested | ../.git | ${PWD}/.git | false | true | ${PWD} | <missing> | ||
${PWD}/.git | . | . | true | false | fatal: this operation must be run in a work tree | <missing> | <missing> | |
${PWD}/.git/worktrees | ${PWD}/.git | ${PWD}/.git | true | false | fatal: this operation must be run in a work tree | <missing> | ||
${PWD}/.git/worktrees/wrktr | ${PWD}/.git | . | true | false | fatal: this operation must be run in a work tree | <missing> | ${PWD}/wrktr/.git | |
${PWD}/.git/worktrees/wrktr/modules/submod | . | . | false | false | ${PWD}/wrktr/submod | ../../../../../wrktr/submod | ../../../../../wrktr/submod | |
${PWD}/wrktr | ${PWD}/.git | ${PWD}/.git/worktrees/wrktr | false | true | ${PWD}/wrktr | <missing> | ||
${PWD}/wrktr/submod | ${PWD}/.git/worktrees/wrktr/modules/submod | ${PWD}/.git/worktrees/wrktr/modules/submod | false | true | ${PWD}/wrktr/submod | ../../../../../wrktr/submod | ||
${PWD}/submod | ${PWD}/.git/modules/submod | ${PWD}/.git/modules/submod | false | true | ${PWD}/submod | ../../../submod | ||
${PWD}/.git/modules/submod | . | . | false | false | ${PWD}/submod | ../../../submod | ../../../submod |
pwd | --git-common-dir | --git-dir | --is-inside-git-dir | --is-inside-work-tree | --show-toplevel | config#core.worktree | ./config#core.worktree | ./gitdir |
|---|---|---|---|---|---|---|---|---|
${PWD}/.git | . | . | true | false | fatal: this operation must be run in a work tree | <missing> | <missing> | |
${PWD}/.git/worktrees | ${PWD}/.git | ${PWD}/.git | true | false | fatal: this operation must be run in a work tree | <missing> | ||
${PWD}/.git/worktrees/wrktr | ${PWD}/.git | . | true | false | fatal: this operation must be run in a work tree | <missing> | ${PWD}/wrktr/.git | |
${PWD}/.git/worktrees/wrktr/modules/submod | . | . | false | false | ${PWD}/wrktr/submod | ../../../../../wrktr/submod | ../../../../../wrktr/submod | |
${PWD}/.git/modules/submod | . | . | false | false | ${PWD}/submod | ../../../submod | ../../../submod |
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
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
That you can configure git to use an alternative git directory using git config core.hooksPath as of git 2.9.
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
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'
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.
That git ships a feature that remembers how you resolve conflicts, git-rerere: https://git-scm.com/book/en/v2/Git-Tools-Rerere.