Set up a local bare git repo
# Create a bare git repo
git init --bare $HOME/.dotfiles
# Config
# "dcf" stanfords for "dotconfig"
alias dcf="/usr/bin/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
dcf remote add origin git@github.com: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
# Get the SSH key somehow
scp or what-so-ever
# Set read only permission
chmod 400 $HOME/.ssh/dcf_rsa*
# Add Git hosting service SSH fingerprint to known_hosts
# Check Meta - GitHub Docs
# https://docs.github.com/en/rest/meta#get-github-meta-information
touch $HOME/.ssh/known_hosts
tee -a $HOME/.ssh/known_hosts &>/dev/null << "EOF"
github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=
github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=
EOF
# 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 git@github.com:Vinfall/dotfiles.git $HOME/.dotfiles
alias dcf="/usr/bin/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 <branch>
# Chores
omz reload
touch $XDG_CONFIG_HOME/wgetrc
mkdir -p $XDG_STATE_HOME/zsh
touch $XDG_STATE_HOME/zsh/history
# Lingering useless dotfiles
rm .bash_history .gitconfig .viminfo .zshrc
Improvement
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:
alias dcf="/usr/bin/git --git-dir $HOME/.dotfiles --work-tree=$HOME"
There are --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 very 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 may try it one day now that the methodology is significantly personalized and improved):
#!/bin/bash
touch $HOME/.ssh/known_hosts
tee -a $HOME/.ssh/known_hosts &>/dev/null << "EOF"
github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=
github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=
EOF
git config --global core.sshCommand "ssh -i ~/.ssh/dcf_rsa"
git clone --bare git@github.com:Vinfall/dotfiles.git $HOME/.dotfiles
function dcf {
/usr/bin/git --git-dir=$HOME/.dotfiles/ --work-tree=$HOME $@
}
rm .config/neofetch/config.conf
mkdir -p .config-backup
dcf checkout main
if [ $? = 0 ]; then
echo "Checked out dotfiles.";
else
echo "Backing up old dotfiles.";
dcf checkout 2>&1 | grep -E "\s+\." | awk {'print $1'} | xargs -I{} mv {} .config-backup/{}
fi;
dcf config --local status.showUntrackedFiles no
dcf config --local core.sshCommand "ssh -i ~/.ssh/dcf_rsa"
dcf checkout main
touch $XDG_CONFIG_HOME/wgetrc
mkdir -p $XDG_STATE_HOME/zsh
touch $XDG_STATE_HOME/zsh/history
rm .bash_history .gitconfig .viminfo .zshrc
References
- 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?