#!/bin/sh # _ # | | # __ __, | | # / \_/ | |/_) # \__/ \_/|_/| \_/ # # ~/.bin/oak # # manage multiple dotfiles repo in same work tree # # Example: # dotfiles.d/ # |_ config # |_ secret # # ~ rgoncalves.se usage() { cat <<- EOF USAGE ${0} : git_dirname git_action [arguments...] -b: enable bootstraping for absent repository -d: oak directory to work on works in batch mode if no directory is provided -f: force all operations use with caution -m: commit message when using -s option commits_msg_have_to_be_written_like_this -r: git remote with full path to parent directory -h: show usage of ${0} EOF } log() { echo "[OAK ] ${@}" } git_chroot() { log ">> ${1}" git --git-dir="${OAK_ROOT}/${1}" --work-tree="${HOME}" ${2} } git_bootstrap() { # directory url remote git_chroot "${1}" "clone --bare git@${2}/${1} ${OAK_ROOT}/${1}" [ "${?}" -ne 0 ] && log "Failed to bootstrap ${1}" && exit 1 git_chroot "${1}" "checkout trunk" >/dev/null 2>&1 git_chroot "${1}" "branch --set-upstream-to trunk origin/trunk" >/dev/null 2>&1 git_chroot "${1}" "push -u origin trunk" >/dev/null 2>&1 } git_sync() { # actions git_sane_all pull_out=$(git_chroot "${1}" "pull") if [ "${?}" -ne 0 ]; then log "error while doing git pull" exit 1 fi echo "${pull_out}" 2>&1 | grep " ." git_chroot "${1}" "add -u" >/dev/null git_chroot "${1}" "commit -m ${OAK_MSG}" git_chroot "${1}" "push --set-upstream origin trunk" # cleanup unset pull_out push_out } git_list() { log "Available repositories : " for dir in ${OAK_DIRS}; do log " - ${dir}" done } git_sane() { if [ "${1}" = "${2}" ]; then return 1 fi git_chroot "${1}" "ls-files" > ~/.cache/oak_a git_chroot "${2}" "ls-files" > ~/.cache/oak_b out=$(grep -F -x -f ~/.cache/oak_a ~/.cache/oak_b) if [ -n "${out}" ]; then log "detected similar files for directories: ${repo_a}, ${repo_b}" echo "${out}" return 1 fi return 0 } git_sane_all() { for repo_a in ${DOT_DIRS}; do for repo_b in ${DOT_DIRS}; do if [ "${repo_a}" = "${repo_b}" ]; then continue fi git_sane "${DOT_ROOT}/${repo_a}" "${DOT_ROOT}/${repo_b}" if [ "${?}" -ne 0 ]; then log "aborting" exit 1 fi done done } main() { OAK_ROOT="${HOME}/.dotfiles.d" OAK_DIRS=$(ls "${OAK_ROOT}") OAK_DIR="" OAK_REMOTE="" OAK_CMD="" OAK_MSG="" # Retrieve oak params while getopts "bd:hlm:r:s" arg; do case "${arg}" in b) OAK_BOOTSTRAP=1 ;; d) OAK_DIR="${OPTARG}" ;; f) OAK_FORCE=1 ;; h) usage && exit 0 ;; m) OAK_MSG="$(echo ${OPTARG} | tr -s '_' ' ')" ;; l) git_list exit 0 ;; r) OAK_REMOTE="${OPTARG}" ;; s) OAK_SYNC=1 ;; esac done [ -z "${OAK_MSG}" ] && OAK_MSG=$(date +%j_%H_%M_%S) # Bootstrap non-present repositories if [ -n "${OAK_DIR}" ] && [ ! -d "${OAK_ROOT}/${OAK_DIR}" ]; then git_bootstrap "${OAK_DIR}" "${OAK_REMOTE}" if [ "${?}" -ne 0 ]; then log "Git repository does not exist. Error" exit 1 fi log "Successfully bootstraped git repository" fi # Retrieve oak command for git shift $(expr "${OPTIND}" - 1) for cmd in "${@}"; do OAK_CMD="${OAK_CMD}${cmd} " done # Git synchronization in each repository if [ -n "${OAK_SYNC}" ]; then for dir in ${OAK_DIR:-$OAK_DIRS}; do log "synchronize ${dir}" git_sync "${dir}" done fi # Raw command in each git repository if [ -n "${OAK_CMD}" ]; then for dir in ${OAK_DIR:-$OAK_DIRS}; do git_chroot "${dir}" "${OAK_CMD}" done fi } main $@