From: Richard Hartmann Date: Sat, 25 Oct 2014 20:33:39 +0000 (+0200) Subject: Merge branch 'feature--clone_branch' X-Git-Url: https://git.madduck.net/code/vcsh.git/commitdiff_plain/93b8c205b18a69c4e5ae6b9ebe425c328e23c17b?hp=-c Merge branch 'feature--clone_branch' Conflicts: vcsh --- 93b8c205b18a69c4e5ae6b9ebe425c328e23c17b diff --combined doc/vcsh.1.ronn index c91f646,e768882..1d432c3 --- a/doc/vcsh.1.ronn +++ b/doc/vcsh.1.ronn @@@ -5,7 -5,7 +5,7 @@@ vcsh(1) - Version Control System for $H `vcsh` [] - `vcsh` clone [] + `vcsh` clone [-b ] [] `vcsh` delete @@@ -17,9 -17,9 +17,9 @@@ `vcsh` list -`vcsh` list-tracked +`vcsh` list-tracked [] -`vcsh` list-tracked-by +`vcsh` list-untracked [<-r>] [] `vcsh` pull @@@ -86,6 -86,9 +86,9 @@@ an interactive user If you need to clone a bundle of repositories, look into the `post-clone-retired` hook. + You can also use a single git repository with several branches. Use the `-b` + option to specify a branch at clone time, the default is `master`. + * commit: Commit in all repositories @@@ -107,24 -110,9 +110,24 @@@ * list-tracked: List all files tracked by vcsh. + If you want to list files tracked by a specific repository, simply + append the repository's name last. + * list-tracked-by: List files tracked by a repository. + This is a legacy command; you should use `list-tracked ` instead. + +* list-untracked: + List all files NOT tracked by vcsh. + + By default, the file list is shallow and stops at directory levels where + possible. If you prefer to get a list of all files, append `-r` for + recursive mode. + + If you want to list files not tracked by a specific repository, simply + append the repository's name last. + * pull: Pull from all vcsh remotes. @@@ -269,24 -257,6 +272,24 @@@ Available hooks are , and +<$XDG_CONFIG_HOME/vcsh/overlays-enabled>. + +Overlays follow the same rules as hooks and you are free to overwrite any +and all functions. Same as hooks, you can use global or repository-specific +overlays by using either <$VCSH_OVERLAY_D/$VCSH_COMMAND> or +<$VCSH_OVERLAY_D/$VCSH_REPO_NAME.$VCSH_COMMAND>. + +Please note that nothing stops you from, e.g. overwriting `status()` in +<$VCSH_OVERLAY_D/commit>. As the overlays will be sourced and you are +replacing arbitrary functions, any and all features may stop working, or you +may even lose data. + +You have been warned. + ## DETAILED HOWTO AND FURTHER READING Manpages are often short and sometimes useless to glean best practices from. diff --combined vcsh index cf7d1a7,4ab17bf..af67e18 --- a/vcsh +++ b/vcsh @@@ -19,12 -19,11 +19,12 @@@ # If '.git-HEAD' is appended to the version, you are seeing an unreleased # version of vcsh; the master branch is supposed to be clean at all times # so you can most likely just use it nonetheless -VERSION='1.20140508' +VERSION='1.20141025' SELF=$(basename $0) fatal() { echo "$SELF: fatal: $1" >&2 + [ -z $2] && exit 1 exit $2 } @@@ -76,7 -75,6 +76,7 @@@ f # Read defaults : ${VCSH_REPO_D:="$XDG_CONFIG_HOME/vcsh/repo.d"} : ${VCSH_HOOK_D:="$XDG_CONFIG_HOME/vcsh/hooks-enabled"} +: ${VCSH_OVERLAY_D:="$XDG_CONFIG_HOME/vcsh/overlays-enabled"} : ${VCSH_BASE:="$HOME"} : ${VCSH_GITIGNORE:=exact} : ${VCSH_GITATTRIBUTES:=none} @@@ -100,7 -98,8 +100,8 @@@ help() -v Enable verbose mode commands: - clone \\ + clone [-b ] \\ + \\ [] Clone from an existing repository commit Commit in all repositories delete Delete an existing repository @@@ -108,10 -107,9 +109,10 @@@ help Display this help text init Initialize a new repository list List all repositories - list-tracked List all files tracked by vcsh - list-tracked-by \\ - List files tracked by a repository + list-tracked \\ + [] List all files tracked all or one repositories + list-untracked \\ + [<-r>] [] List all files not tracked by all or one repositories pull Pull from all vcsh remotes push Push to vcsh remotes rename \\ @@@ -149,16 -147,16 +150,16 @@@ clone() hook pre-clone init git remote add origin "$GIT_REMOTE" - git config branch.master.remote origin - git config branch.master.merge refs/heads/master - VCSH_CLONE_ERROR=$(git ls-remote origin master 2>&1) - if [ -n "$VCSH_CLONE_ERROR" ]; then - rm -rf "$GIT_DIR" - fatal "$VCSH_CLONE_ERROR" 1 + git checkout -b "$VCSH_BRANCH" || return $? + git config branch."$VCSH_BRANCH".remote origin + git config branch."$VCSH_BRANCH".merge refs/heads/"$VCSH_BRANCH" + if [ $(git ls-remote origin "$VCSH_BRANCH" 2> /dev/null | wc -l ) -lt 1 ]; then + info "remote is empty, not merging anything" + exit fi - git fetch + git fetch origin "$VCSH_BRANCH" hook pre-merge - git ls-tree -r --name-only origin/master | (while read object; do + git ls-tree -r --name-only origin/"$VCSH_BRANCH" | (while read object; do [ -e "$object" ] && error "'$object' exists." && VCSH_CONFLICT=1 @@@ -166,7 -164,7 +167,7 @@@ [ x"$VCSH_CONFLICT" = x'1' ]) && fatal "will stop after fetching and not try to merge! Once this situation has been resolved, run 'vcsh $VCSH_REPO_NAME pull' to finish cloning." 17 - git merge origin/master + git merge origin/"$VCSH_BRANCH" hook post-merge hook post-clone retire @@@ -246,71 -244,16 +247,71 @@@ get_files() } list_tracked() { - for VCSH_REPO_NAME in $(list); do - get_files - done | sed "s,^,$(printf '%s\n' "$VCSH_BASE/" | \ - sed 's/[,\&]/\\&/g')," | sort -u + VCSH_REPO_NAME=$2; export VCSH_REPO_NAME + if [ -n "$VCSH_REPO_NAME" ]; then + get_files | list_tracked_helper + else + for VCSH_REPO_NAME in $(list); do + get_files + done | list_tracked_helper + fi +} + +list_tracked_helper() { + sed "s,^,$(printf '%s\n' "$VCSH_BASE/" | sed 's/[,\&]/\\&/g')," | sort -u } list_tracked_by() { - use - git ls-files | sed "s,^,$(printf '%s\n' "$VCSH_BASE/" | \ - sed 's/[,\&]/\\&/g')," | sort -u + list_tracked $2 +} + +list_untracked() { + command -v 'comm' >/dev/null 2>&1 || fatal "Could not find 'comm'" + + temp_file_others=$(mktemp "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX") || fatal 'Could not create temp file' + temp_file_untracked=$(mktemp "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX") || fatal 'Could not create temp file' + temp_file_untracked_copy=$(mktemp "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX") || fatal 'Could not create temp file' + + # Hack in support for `vcsh list-untracked -r`... + directory_opt="--directory" + shift 1 + while getopts "r" flag; do + if [ x"$1" = x'-r' ]; then + unset directory_opt + fi + shift 1 + done + # ...and parse for a potential parameter afterwards. As we shifted things out of $* in during getops, we need to look at $1 + VCSH_REPO_NAME=$1; export VCSH_REPO_NAME + + if [ -n "$VCSH_REPO_NAME" ]; then + list_untracked_helper $VCSH_REPO_NAME + else + for VCSH_REPO_NAME in $(list); do + list_untracked_helper $VCSH_REPO_NAME + done + fi + cat $temp_file_untracked + + unset directory_opt directory_component + rm -f $temp_file_others $temp_file_untracked $temp_file_untracked_copy || fatal 'Could not delete temp files' +} + +list_untracked_helper() { + export GIT_DIR="$VCSH_REPO_D/$VCSH_REPO_NAME.git" + git ls-files --others "$directory_opt" | ( + while read line; do + echo "$line" + directory_component=${line%%/*} + [ -d "$directory_component" ] && printf '%s/\n' "$directory_component" + done + ) | sort -u > $temp_file_others + if [ -z "$ran_once" ]; then + ran_once=1 + cp $temp_file_others $temp_file_untracked || fatal 'Could not copy temp file' + fi + cp $temp_file_untracked $temp_file_untracked_copy || fatal 'Could not copy temp file' + comm -12 --nocheck-order $temp_file_others $temp_file_untracked_copy > $temp_file_untracked } pull() { @@@ -366,29 -309,22 +367,29 @@@ run() status() { if [ -n "$VCSH_REPO_NAME" ]; then - GIT_DIR=$VCSH_REPO_D/$VCSH_REPO_NAME.git; export GIT_DIR - use - git status --short --untracked-files='no' - VCSH_COMMAND_RETURN_CODE=$? + status_helper $VCSH_REPO_NAME else for VCSH_REPO_NAME in $(list); do echo "$VCSH_REPO_NAME:" - GIT_DIR=$VCSH_REPO_D/$VCSH_REPO_NAME.git; export GIT_DIR - use - git status --short --untracked-files='no' - VCSH_COMMAND_RETURN_CODE=$? + status_helper $VCSH_REPO_NAME echo done fi } +status_helper() { + GIT_DIR=$VCSH_REPO_D/$VCSH_REPO_NAME.git; export GIT_DIR + use + remote_tracking_branch=$(git rev-parse --abbrev-ref --symbolic-full-name @{u} 2> /dev/null) && { + commits_behind=$(git log ..${remote_tracking_branch} --oneline | wc -l) + commits_ahead=$(git log ${remote_tracking_branch}.. --oneline | wc -l) + [ ${commits_behind} -ne 0 ] && echo "Behind $remote_tracking_branch by $commits_behind commits" + [ ${commits_ahead} -ne 0 ] && echo "Ahead of $remote_tracking_branch by $commits_ahead commits" + } + git status --short --untracked-files='no' + VCSH_COMMAND_RETURN_CODE=$? +} + upgrade() { hook pre-upgrade # fake-bare repositories are not bare, actually. Set this to false @@@ -417,7 -353,6 +418,7 @@@ use() } which() { + [ -e "$VCSH_COMMAND_PARAMETER" ] || fatal "'$VCSH_COMMAND_PARAMETER' does not exist" 1 for VCSH_REPO_NAME in $(list); do for VCSH_FILE in $(get_files); do echo "$VCSH_FILE" | grep -q "$VCSH_COMMAND_PARAMETER" && echo "$VCSH_REPO_NAME: $VCSH_FILE" @@@ -497,11 -432,28 +498,28 @@@ case $VCSH_COMMAND i esac if [ x"$VCSH_COMMAND" = x'clone' ]; then + VCSH_BRANCH= + if [ "$2" = -b ]; then + VCSH_BRANCH=$3 + shift + shift + fi [ -z "$2" ] && fatal "$VCSH_COMMAND: please specify a remote" 1 GIT_REMOTE="$2" - [ -n "$3" ] && VCSH_REPO_NAME=$3 || VCSH_REPO_NAME=$(basename "${GIT_REMOTE#*:}" .git) + [ -n "$VCSH_BRANCH" ] || if [ "$3" = -b ]; then + VCSH_BRANCH=$4 + shift + shift + fi + if [ -n "$3" ]; then + VCSH_REPO_NAME=$3 + [ -z "$VCSH_BRANCH" ] && [ "$4" = -b ] && VCSH_BRANCH=$5 + else + VCSH_REPO_NAME=$(basename "${GIT_REMOTE#*:}" .git) + fi [ -z "$VCSH_REPO_NAME" ] && fatal "$VCSH_COMMAND: could not determine repository name" 1 export VCSH_REPO_NAME + [ -n "$VCSH_BRANCH" ] || VCSH_BRANCH=master GIT_DIR=$VCSH_REPO_D/$VCSH_REPO_NAME.git; export GIT_DIR elif [ "$VCSH_COMMAND" = 'version' ]; then echo "$SELF $VERSION" @@@ -530,7 -482,6 +548,7 @@@ elif [ x"$VCSH_COMMAND" = x'delete' elif [ x"$VCSH_COMMAND" = x'commit' ] || [ x"$VCSH_COMMAND" = x'list' ] || [ x"$VCSH_COMMAND" = x'list-tracked' ] || + [ x"$VCSH_COMMAND" = x'list-untracked' ] || [ x"$VCSH_COMMAND" = x'pull' ] || [ x"$VCSH_COMMAND" = x'push' ]; then : @@@ -578,17 -529,6 +596,17 @@@ check_dir "$VCSH_REPO_D verbose "$VCSH_COMMAND begin" VCSH_COMMAND=$(echo "$VCSH_COMMAND" | sed 's/-/_/g'); export VCSH_COMMAND + +# Source repo-specific configuration file +[ -r "$XDG_CONFIG_HOME/vcsh/config.d/$VCSH_REPO_NAME" ] && . "$XDG_CONFIG_HOME/vcsh/config.d/$VCSH_REPO_NAME" + +# source overlay functions +for overlay in "$VCSH_OVERLAY_D/$VCSH_COMMAND"* "$VCSH_OVERLAY_D/$VCSH_REPO_NAME.$VCSH_COMMAND"*; do + [ -r "$overlay" ] || continue + info "sourcing '$overlay'" + . "$overlay" +done + hook pre-command $VCSH_COMMAND "$@" hook post-command