-vcsh(1) - manage config files in $HOME via fake bare git repositories
-=====================================================================
+vcsh(1) - Version Control System for $HOME - multiple Git repositories in $HOME
+===============================================================================
## SYNOPSIS
`vcsh` list-tracked-by <repo>
+`vcsh` pull
+
+`vcsh` push
+
`vcsh` rename <repo> <newname>
`vcsh` run <repo> <shell command>
can have one repository per config set (zsh, vim, ssh, etc), picking and
choosing which configs you want to use on which machine.
-`vcsh` is using a technique called fake bare git repositories, keeping <$GIT_DIR>
+`vcsh` is using a technique called fake bare Git repositories, keeping <$GIT_DIR>
in a different directory from <$GIT_WORK_TREE> which is pointed to <$HOME>.
The use of symlinks is not needed in this setup, making for a cleaner setup.
* list-tracked-by:
List files tracked by a repository.
+* pull:
+ Pull from all vcsh remotes.
+
+* push:
+ Push to all vcsh remotes.
+
* rename:
Rename a repository.
Find <substring> in name of any tracked file.
* write-gitignore:
- Write .gitignore.d/<repo> via git ls-files.
+ Write .gitignore.d/<repo> via `git ls-files`.
* <repo> <gitcommand>:
Shortcut to run `vcsh` on a repo. Will prepend `git` to <command>.
## ENVIRONMENT
As noted earlier, `vcsh` will set <$GIT_DIR> and <$GIT_WORK_TREE> to the
-appropriate values for fake bare git repositories.
+appropriate values for fake bare Git repositories.
## CONFIG
Interesting knobs you can turn:
* <$VCSH_GITIGNORE>:
- Can be either <exact> or <recursive>.
+ Can be <exact>, <recursive>, or <none>.
- <exact> will seed the repo-specific <.gitignore> with all file and directory
+ <exact> will seed the repo-specific ignore file with all file and directory
names which `git ls-files` returns.
<recursive> will descend through all directories recursively additionally to
the above.
+ <none> will not write any ignore file.
+
Defaults to <exact>.
Less interesting knobs you could turn:
Like most people, the author initially made do with a single repository for all
config files, all of which were soft-linked into <$HOME>.
-Martin F. Krafft aka madduck came up with the concept of fake bare git
+Martin F. Krafft aka madduck came up with the concept of fake bare Git
repositories.
vcsh was initally written by madduck. This version is a re-implementation from
## COPYRIGHT
-Copyright 2011-2012 Richard Hartmann <richih.mailinglist@gmail.com>
+Copyright 2011-2013 Richard Hartmann <richih.mailinglist@gmail.com>
Licensed under the GNU GPL version 2 or higher.
}
SELF=$(basename $0)
-VERSION='1.2'
+VERSION='1.20130614'
fatal() {
echo "$SELF: fatal: $1" >&2
list-tracked List all files tracked by vcsh
list-tracked-by \\
<repo> List files tracked by a repository
+ pull Pull from all vcsh remotes
+ push Push to vcsh remotes
rename <repo> \\
<newname> Rename repository
run <repo> \\
}
clone() {
+ hook pre-clone
init
git remote add origin "$GIT_REMOTE"
git config branch.master.remote origin
fatal "will stop after fetching and not try to merge!
Once this situation has been resolved, run 'vcsh run $VCSH_REPO_NAME git pull' to finish cloning.\n" 17
git merge origin/master
+ hook post-clone
}
delete() {
git ls-files | sort -u
}
+pull() {
+ hook pre-pull
+ for VCSH_REPO_NAME in $(list); do
+ echo -n "$VCSH_REPO_NAME: "
+ export GIT_DIR="$VCSH_REPO_D/$VCSH_REPO_NAME.git"
+ use
+ git pull
+ done
+ hook post-pull
+}
+
+push() {
+ hook pre-push
+ for VCSH_REPO_NAME in $(list); do
+ echo -n "$VCSH_REPO_NAME: "
+ export GIT_DIR="$VCSH_REPO_D/$VCSH_REPO_NAME.git"
+ use
+ git push
+ done
+ hook post-push
+}
+
rename() {
git_dir_exists
[ -d "$GIT_DIR_NEW" ] && fatal "'$GIT_DIR_NEW' exists" 54
hook pre-upgrade
use
git config core.worktree "$GIT_WORK_TREE"
- git config core.excludesfile ".gitignore.d/$VCSH_REPO_NAME"
+ [ ! "x$VCSH_GITIGNORE" = 'xnone' ] && git config core.excludesfile ".gitignore.d/$VCSH_REPO_NAME"
git config vcsh.vcsh 'true'
[ -e "$VCSH_BASE/.gitignore.d/$VCSH_REPO_NAME" ] && git add -f "$VCSH_BASE/.gitignore.d/$VCSH_REPO_NAME"
hook post-upgrade
}
write_gitignore() {
+ # Don't do anything if the user does not want to write gitignore
+ if [ "x$VCSH_GITIGNORE" = 'xnone' ]; then
+ info "Not writing gitignore as '\$VCSH_GITIGNORE' is set to 'none'"
+ exit
+ fi
+
use
cd "$VCSH_BASE" || fatal "could not enter '$VCSH_BASE'" 11
gitignores=$(for file in $(git ls-files); do
echo '*' > "$tempfile" || fatal "could not write to '$tempfile'" 57
for gitignore in $gitignores; do
echo "$gitignore" | sed 's@^@!/@' >> "$tempfile" || fatal "could not write to '$tempfile'" 57
- if [ x$VCSH_GITIGNORE = x'recursive' ] && [ -d "$gitignore" ]; then
+ if [ "x$VCSH_GITIGNORE" = 'xrecursive' ] && [ -d "$gitignore" ]; then
{ echo "$gitignore/*" | sed 's@^@!/@' >> "$tempfile" || fatal "could not write to '$tempfile'" 57; }
fi
done
fatal "could not move '$tempfile' to '$VCSH_BASE/.gitignore.d/$VCSH_REPO_NAME'" 53
}
- if [ ! "x$VCSH_GITIGNORE" = 'xexact' ] && [ ! "x$VCSH_GITIGNORE" = 'xrecursive' ]; then
- fatal "'\$VCSH_GITIGNORE' must be either 'exact' or 'recursive'" 1
+ if [ ! "x$VCSH_GITIGNORE" = 'xexact' ] && [ ! "x$VCSH_GITIGNORE" = 'xrecursive' ] && [ ! "x$VCSH_GITIGNORE" = 'xnone' ]; then
+ fatal "'\$VCSH_GITIGNORE' must equal 'exact', 'recursive', or 'none'" 1
fi
if [ "$1" = 'clone' ]; then
[ "$1" = 'write-gitignore' ]; then
[ -z $2 ] && fatal "$1: please specify repository to work on" 1
[ "$1" = 'rename' -a -z "$3" ] && fatal "$1: please specify a target name" 1
- [ "$1" = 'run' -a -z "$3" ] && fatal "$1: please specify a command" 1
+ [ "$1" = 'run' -a -z "$3" ] && fatal "$1: please specify a command" 1
export VCSH_COMMAND="$1"
export VCSH_REPO_NAME="$2"
export GIT_DIR="$VCSH_REPO_D/$VCSH_REPO_NAME.git"
- [ "$VCSH_COMMAND" = 'rename' ] && export GIT_DIR_NEW="$VCSH_REPO_D/$3.git"
- [ "$VCSH_COMMAND" = 'run' ] && shift 2
+ [ "$VCSH_COMMAND" = 'rename' ] && export GIT_DIR_NEW="$VCSH_REPO_D/$3.git"
+ [ "$VCSH_COMMAND" = 'run' ] && shift 2
[ "$VCSH_COMMAND" = 'write-gitignore' ]
elif [ "$1" = 'list' ] ||
- [ "$1" = 'list-tracked' ]; then
+ [ "$1" = 'list-tracked' ] ||
+ [ "$1" = 'pull' ] ||
+ [ "$1" = 'push' ]; then
export VCSH_COMMAND="$1"
elif [ -n "$2" ]; then
export VCSH_COMMAND='run'
export VCSH_REPO_NAME=$(basename "$VCSH_REPO_NAME" .git)
fi
-
- for check_directory in "$VCSH_REPO_D" "$VCSH_BASE/.gitignore.d"
- do
+ check_dir() {
+ check_directory="$1"
if [ ! -d "$check_directory" ]; then
if [ -e "$check_directory" ]; then
fatal "'$check_directory' exists but is not a directory" 13
mkdir -p "$check_directory" || fatal "could not create '$check_directory'" 50
fi
fi
- done
+ }
+
+ check_dir "$VCSH_REPO_D"
+ [ ! "x$VCSH_GITIGNORE" = 'xnone' ] && check_dir "$VCSH_BASE/.gitignore.d"
verbose "$VCSH_COMMAND begin"
export VCSH_COMMAND=$(echo $VCSH_COMMAND | sed 's/-/_/g')