Everything in this post is outdated. Look for somewhere else.
Set up a Local Bare Git Repo
# Create a bare git repo
git init --bare "$HOME"/.dotfiles
# Config
# "dcf" stanfords for "dotconfig"
dcf() {
git --git-dir="$HOME"/.dotfiles --work-tree="$HOME" "$@"
}
# Ignore untracked files in $HOME
dcf config --local status.showUntrackedFiles no
# Move untracked dotfiles
mkdir "$HOME"/.dotfiles/zsh
cp "$ZSH_CUSTOM"/zsh/*.zsh "$HOME"/.dotfiles/zsh/
rm "$HOME"/.dotfiles/zsh/example.zsh
# Add files
dcf add "$HOME"/.*shrc
dcf add "$HOME"/.p10k.zsh
dcf add "$HOME"/.vimrc
dcf add "$HOME"/.dotfiles/zsh/*.zsh
# Check status
dcf status
# Be sure to check branch when committing
dcf commit
# For different dotfiles set (Desktop/Server/WSL/Termux)
dcf branch void
dcf checkout void
dcf rm "$HOME"/.dotfiles/zsh/deb.zsh
...
dcf commit
Push to Git Hosting Website
Create a repo in GitHub web (make sure do NOT add a README as this will create a main branch) beforehand.
# Store the key as dcf_rsa
ssh-keygen -t rsa -b 4096
# Copy the pubkey and add as deploy key in the REPO settings
cat ~/.ssh/dcf_rsa.pub | co
# gh: is an predefined alias
dcf remote add origin gh:Vinfall/dotfiles.git
# Add
dcf config core.sshcommand "ssh -i ~/.ssh/dcf_rsa"
# Should set up remote when pushing for the first time
dcf checkout void
dcf push --set-upstream origin void
dcf checkout server
dcf push --set-upstream origin server
...
# No need to set upstream next time
dcf checkout void
dcf push
Restore in a New System
Setup
[another@machine]~$ rsync -avz --ignore-existing -e ssh ~/.ssh/dcf* remote:~/.ssh/
Recover
# Setup GnuPG first, you don't need to do this
rm -rf "$HOME"/.gnupg
mkdir -p "$HOME"/.local/share/gnupg
export GNUPGHOME="$HOME"/.local/share/gnupg
timeout 5 gpg --homedir "$HOME"/.local/share/gnupg
chmod 600 ~/.local/share/gnupg/*
chmod 700 ~/.local/share/gnupg
# Add ignore to avoid weird recursion problems (No need if not created the folders before)
#echo ".dotfiles" >> .gitignore
# Clone
# No need to worry about the global flag, we'll discard .gitconfig before checkouting out the branch anyway
git config --global core.sshCommand "ssh -i ~/.ssh/dcf_rsa"
git clone --bare gh:Vinfall/dotfiles.git "$HOME"/.dotfiles
dcf() {
git --git-dir="$HOME"/.dotfiles --work-tree="$HOME" "$@"
}
# Backup orig dotfiles in conflict
mkdir -p .config-backup && \
dcf checkout 2>&1 | grep -E "\s+\." | awk '{print $1}' | \
xargs -I{} mv {} .config-backup/{}
# Setup local git config again
dcf config --local status.showUntrackedFiles no
dcf config --local core.sshCommand "ssh -i ~/.ssh/dcf_rsa"
dcf checkout main
# Chores
omz reload
touch "$XDG_CONFIG_HOME"/wgetrc
echo hsts-file \= "$XDG_CACHE_HOME"/wget-hsts >> "$XDG_CONFIG_HOME/wgetrc"
mkdir -p "$XDG_STATE_HOME"/{bash,zsh}
touch "$XDG_STATE_HOME"/{bash,zsh}/history
mkdir -p "$XDG_DATA_HOME"/VSCodium/extensions
# Lingering useless dotfiles
rm .bash_history .gitconfig .viminfo .zshrc
# Desktop
rm -r ~/Downloads
xdg-user-dirs-update
# after reboot
ll "$HOME"/.cache/ICEauthority
rm "$HOME"/.ICEauthority
Improvement
Deprecated.
Use $ZSH_CUSTOM
Use $ZSH_CUSTOM to avoid manually link/copy *.zsh into the custom plugin folder.
Lazy-load Custom Plugin
Merge multiple branches into main is tedious ~~which can be avoided by lazy-loading zsh custom plugins ~~. Although I do not lazy-load plugins, I do write several if statement to make sure dumb zsh doesn’t load unused custom plugins:
# slackware.zsh
# If `os-release` contains slackware
if [[ -f /etc/os-release ]]; then
if cat /etc/os-release | grep --quiet --ignore-case --extended-regexp "slackware"; then
alias new-alias="whatever-command"
fi
fi
# wsl.zsh
# If kernel name contains wsl
if uname -r | grep --quiet --ignore-case --extended-regexp "wsl"; then
alias new-alias="whatever-command"
fi
# termux.zsh
# If $TERMUX_VERSION is not null
if [[ -n $TERMUX_VERSION ]];then
alias new-alias="whatever-command"
fi
Add .gitignore
Add .gitignore to avoid accidentally adding whole $HOME to repo and (again) accidentally stashed innocent dotfiles, which would cause severe damage & take ages to recover w/o backup.
This is not as straightforward as it seems to be:
dcf() {
git --git-dir="$HOME"/.dotfiles --work-tree="$HOME" "$@"
}
With --git-dir and --work-tree, we should put .gitignore in --work-tree aka $HOME.
It’s a bit ugly and actually I wish to switch --work-tree
now that I’m aware of XDG Base Directory Specification,
so I won’t have yet another file in $HOME.
An example .gitignore(N.B. I don’t version control files inside $XDG_DATA_HOME due to privacy leakage concern, YMMV):
# .gitignore
.cache
.gnupg
.ssh
.config/zsh/.zcompdump*
.config/zsh/oh-my-zsh
.local/share
.local/state
Remove Unused Objects
After managing dotfiles for nearly a month,
I notice a lot of orphan objects in .dotfiles/objects/* when running dcf status -u
(thanks to the optimization of Lazy-load custom plugin).
Git is so complicated that it can never have too many ways to achieve this.
I just cowardly run git repack && git prune-packed to remove most of the unused objects.
Now dcf status -u returns few results.
One More Thing
It’s also possible to store the whole restoring commands as a code snippet. Create a short URL and make a call like:
curl -Lks https://short.link/dcf-restore | /bin/bash
Adding the respective git hosting site IP to known_hosts before git clone can have one less manual input.
The code snippet (which I did NOT use as my infrastructure is far more complicated instead of using the same distro I use it now that the methodology is significantly personalized and improved) is redacted since it’s basically a rewrite of #Recover. You just need to split the script into several functions (aka. stages) and consider carefully about variables and files available in each stage.
Reference
- Dotfiles 管理 - 使用 git 裸仓库
- The best way to store your dotfiles: A bare Git repository
- How to move a local bare repository to GitHub
- How to configure a local Git repository to use a specific SSH key
- Why should I use core.autocrlf=true in Git?
- Can I use a .gitignore with a bare repo?
- Using a bare Git repo to get version control for my dotfiles
- How to remove unused objects from a git repository?
