From 69809482358e183bc8fc7e0a85331533e4ceb966 Mon Sep 17 00:00:00 2001 From: Fabien Dubosson Date: Thu, 12 Mar 2015 23:42:50 +0100 Subject: [PATCH 01/18] Bump develop version --- src/gws | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gws b/src/gws index f0d9c71..b946f9a 100755 --- a/src/gws +++ b/src/gws @@ -5,7 +5,7 @@ # OS: Probably all linux distributions # # Requirements: git, bash > 4.0 # # License: MIT (See below) # -# Version: 0.1.7 # +# Version: 0.1.8-DEV # # # # 'gws' is the abbreviation of 'Git WorkSpace'. # # This is an helper to manage workspaces which contain git repositories. # @@ -50,7 +50,7 @@ set -o pipefail # {{{ Parameters # Version number -VERSION="0.1.7" +VERSION="0.1.8-DEV" # Starting directory START_PWD="$(pwd)" From be7c16075fddede527232cd093a8f21bd38b558a Mon Sep 17 00:00:00 2001 From: Fabien Dubosson Date: Fri, 27 Mar 2015 00:43:26 +0100 Subject: [PATCH 02/18] Allow a second argument to specify a subfolder Close #14 --- CHANGELOG | 5 +++++ TODO | 1 - src/gws | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 60 insertions(+), 5 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 84377c7..33a7573 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,8 @@ +0.1.8 +===== + +* A second argument can be given to limit to a specific subfolder of the workspace + 0.1.6 ===== diff --git a/TODO b/TODO index f092d55..7efa370 100644 --- a/TODO +++ b/TODO @@ -1,2 +1 @@ -* Permit a second command-line argument to restrict the folder (e.g. gws update work, gws status ., ...) * Check command: ignore recursively folder containing a workspace (.projects.gws) diff --git a/src/gws b/src/gws index b946f9a..c840f69 100755 --- a/src/gws +++ b/src/gws @@ -198,6 +198,32 @@ function remove_prefixed() return 0 } +# Keep projects that are prefixed by the given directory +function keep_prefixed_projects() +{ + local limit_to dir current + + # First check if the folder exists + [[ ! -d "${START_PWD}/$1" ]] && return 1 + + # Get the full path to limit to in regexp form + limit_to=$(cd "${START_PWD}/$1" && pwd )/ + limit_to=$(sed -e 's/[]\/()$*.^|[]/\\&/g' <<< "$limit_to") + + # Iterate over each project + for dir in "${projects_indexes[@]}" + do + # Get its full path + current=$(cd $dir; pwd)/ + + # If it match, add it to the output + [[ $current =~ ^$limit_to ]] && echo -n "$dir " + done + + # Everything is right + return 0 +} + # }}} # {{{ Projects functions @@ -813,15 +839,22 @@ function usage() { echo -e "gws is an helper to manage workspaces which contain git repositories." echo -e "" - echo -e "Usage: ${C_RED}$(basename "$0")${C_OFF} ${C_BLUE}${C_OFF}" + echo -e "Usages: ${C_RED}$(basename "$0")${C_OFF} ${C_BLUE}${C_OFF} [${C_GREEN}${C_OFF}]" + echo -e " ${C_RED}$(basename "$0")${C_OFF} [${C_GREEN}${C_OFF}]" echo -e "" - echo -e "where is:" + echo -e "where ${C_BLUE}${C_OFF} is:" echo -e " ${C_BLUE}init${C_OFF} - Detect the repositories and create the projects list" echo -e " ${C_BLUE}update${C_OFF} - Update the workspace to get new repositories from projects list" echo -e " ${C_BLUE}status${C_OFF} - Print status for all repositories in the workspace" echo -e " ${C_BLUE}fetch${C_OFF} - Print status for all repositories in the workspace, but fetch the origin before" echo -e " ${C_BLUE}ff${C_OFF} - Print status for all repositories in the workspace, but fast forward from origin before" echo -e " ${C_BLUE}check${C_OFF} - Check the workspace for all repositories (known/unknown/missing)" + echo -e "" + echo -e "If no ${C_BLUE}${C_OFF} is specified, the command ${C_BLUE}status${C_OFF} is assumed." + echo -e "" + echo -e "where ${C_GREEN}${C_OFF} can be a path to limit the scope of the commands to a specific subfolder" + echo -e "of the workspace." + echo -e "" exit 1 } @@ -844,6 +877,14 @@ fi # Remove ignored projects projects_indexes=( $(remove_matching projects_indexes[@] ignored_patterns[@]) ) +# If a path is specified as second argument, limit projects to the ones matching +# the path +if [[ -n "$2" ]]; then + error_msg="${C_RED}The directory '$2' is not found.${C_OFF}" + projects_list=$(keep_prefixed_projects "$2") || (echo -e "$error_msg" && exit 1) || exit 1 + projects_indexes=( ${projects_list} ) +fi + # Finally select the desired command case $1 in "init") @@ -852,7 +893,7 @@ case $1 in "update") cmd_update ;; - ""|"status") + "status") cmd_status $S_NONE ;; "fetch") @@ -867,7 +908,17 @@ case $1 in "--version"|"-v") echo -e "gws version ${C_RED}$VERSION${C_OFF}" ;; - *) usage ;; + "--help"|"-h") + usage + ;; + *) + if [[ -n "$1" ]]; then + error_msg="${C_RED}The directory '$1' is not found and is not a recognized command.${C_OFF}" + projects_list=$(keep_prefixed_projects "$1") || (echo -e "$error_msg" && exit 1) || exit 1 + projects_indexes=( ${projects_list} ) + fi + cmd_status $S_NONE + ;; esac # vim: fdm=marker From 6941870b6b093a50a3799d5cf3a2d8cd7e7174b2 Mon Sep 17 00:00:00 2001 From: Fabien Dubosson Date: Fri, 27 Mar 2015 14:42:24 +0100 Subject: [PATCH 03/18] Adapt completions to provide filenames as 2nd arg --- completions/bash | 10 ++++++++-- completions/zsh | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/completions/bash b/completions/bash index c831f2f..7fefcdf 100644 --- a/completions/bash +++ b/completions/bash @@ -3,7 +3,13 @@ _gws() { local cur="${COMP_WORDS[COMP_CWORD]}" - COMPREPLY=( $(compgen -W "init update status fetch ff check" -- $cur) ) + + case $COMP_CWORD in + 1) COMPREPLY=( $(compgen -W "init update status fetch ff check" -- $cur) ) + ;; + 2) COMPREPLY=( $(compgen -f "$cur") ) + ;; + esac } -complete -F _gws gws \ No newline at end of file +complete -F _gws gws diff --git a/completions/zsh b/completions/zsh index e23a722..0b148af 100644 --- a/completions/zsh +++ b/completions/zsh @@ -1,4 +1,4 @@ #compdef gws # Zsh completion for gws -_arguments "1: :(init update status fetch ff check)" +_arguments "1:commands:(init update status fetch ff check)" "2:filenames:_files" From 55bdb57ebfc6627dd81406f0080a6e0805198f43 Mon Sep 17 00:00:00 2001 From: Fabien Dubosson Date: Sun, 14 Jun 2015 16:40:13 +0200 Subject: [PATCH 04/18] Add TODO item --- TODO | 1 + 1 file changed, 1 insertion(+) diff --git a/TODO b/TODO index 7efa370..ac63381 100644 --- a/TODO +++ b/TODO @@ -1 +1,2 @@ * Check command: ignore recursively folder containing a workspace (.projects.gws) +* Cache based on config file hashsum to avoid parsing each time From 8f6ce5e3dd09b8f9b03b51c53c4fe343f2069efa Mon Sep 17 00:00:00 2001 From: Fabien Dubosson Date: Sun, 14 Jun 2015 16:41:48 +0200 Subject: [PATCH 05/18] Parse multiple remotes with names and defaults --- src/gws | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/gws b/src/gws index c840f69..9bfde3b 100755 --- a/src/gws +++ b/src/gws @@ -64,6 +64,9 @@ IGNORE_FILE=".ignore.gws" # Field separator in the projects list FIELD_SEP='|' +# Separator between the URL and its name in config file +URL_NAME_SEP=' ' + # Git name of the origin branch of repositories GIT_ORIGIN="origin" @@ -259,7 +262,7 @@ function exists_project() # Read the list of projects from the projects list file function read_projects() { - local dir repo upstream + local line dir remotes count repo upstream # Read line by line (discard comments and empty lines) while read line @@ -267,6 +270,39 @@ function read_projects() # Remove inline comments line=$(sed -e 's/#.*$//' <<< "$line") + # We get the directory + dir=$(cut -d${FIELD_SEP} -f1 <<< "$line" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') + # We get the rest of the configuration line containing remotes + remotes=$(cut -d${FIELD_SEP} -f1 --complement <<< "$line") + + # We get all the remotes + count=0 + while [ -n "$remotes" ]; + do + count=$((count + 1)) + # We get the first defined remote in the "remotes" variable + remote=$(cut -d${FIELD_SEP} -f1 <<< "$remotes" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' -e 's/[[:space:]]\+/ /g') + # We remove the current remote from the line for next iteration + remotes=$(cut -d${FIELD_SEP} -f1 -s --complement <<< "$remotes") + # We get its url + remote_url=$(cut -d"${URL_NAME_SEP}" -f1 <<< "$remote" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') + # We get its name if any + remote_name=$(cut -d"${URL_NAME_SEP}" -f2 -s <<< "$remote") + + # If name its not set we correct it + if [[ -z "$remote_name" ]]; then + if [[ $count == 1 ]]; then + remote_name=$GIT_ORIGIN + elif [[ $count == 2 ]]; then + remote_name=$GIT_UPSTREAM + else + error_msg="${C_RED}The URL at position $count for \"$dir\" is missing a name.${C_OFF}" + echo -e "$error_msg" + exit 1 + fi + fi + done + # Read the line fields dir=$(cut -d${FIELD_SEP} -f1 <<< "$line" | tr -d '[[:space:]]') repo=$(cut -d${FIELD_SEP} -f2 <<< "$line" | tr -d '[[:space:]]') From 28a016f47dc7264c05e4437c569e1688227bf1cd Mon Sep 17 00:00:00 2001 From: Fabien Dubosson Date: Sun, 14 Jun 2015 16:44:20 +0200 Subject: [PATCH 06/18] Change function git_add_upstream to git_add_remote --- src/gws | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gws b/src/gws index 9bfde3b..11650cf 100755 --- a/src/gws +++ b/src/gws @@ -442,12 +442,12 @@ function git_fast_forward() } # Add an upstream branch to a repository -function git_add_upstream() +function git_add_remote() { local cmd # Git command to execute - cmd=( "git" "remote" "add" "${GIT_UPSTREAM}" "$2") + cmd=( "git" "remote" "add" "$2" "$3") # Run the command and print the output in case of error if ! output=$(cd "$1" && "${cmd[@]}"); then @@ -652,7 +652,7 @@ function cmd_update() fi # If an upstream url is set, add it - [[ ! -z "$upstream" ]] && git_add_upstream "$dir" "$upstream" + [[ ! -z "$upstream" ]] && git_add_remote "$dir" "$GIT_UPSTREAM" "$upstream" printf "${INDENT}%-${MBL}s${C_GREEN} %s${C_OFF}\n" " " "Cloned" done From 44c2b1f2b543e0e849884247d825d1c21acf0d02 Mon Sep 17 00:00:00 2001 From: Fabien Dubosson Date: Sun, 14 Jun 2015 17:03:25 +0200 Subject: [PATCH 07/18] Prevent `init` subcommand to call useless code --- src/gws | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/gws b/src/gws index c840f69..75e0793 100755 --- a/src/gws +++ b/src/gws @@ -871,20 +871,20 @@ if [[ "$1" != "init" ]]; then # Then read the list of projects and ignore list read_projects read_ignored -fi - -# Remove ignored projects -projects_indexes=( $(remove_matching projects_indexes[@] ignored_patterns[@]) ) + # Remove ignored projects + projects_indexes=( $(remove_matching projects_indexes[@] ignored_patterns[@]) ) -# If a path is specified as second argument, limit projects to the ones matching -# the path -if [[ -n "$2" ]]; then - error_msg="${C_RED}The directory '$2' is not found.${C_OFF}" - projects_list=$(keep_prefixed_projects "$2") || (echo -e "$error_msg" && exit 1) || exit 1 - projects_indexes=( ${projects_list} ) + # If a path is specified as second argument, limit projects to the ones matching + # the path + if [[ -n "$2" ]]; then + error_msg="${C_RED}The directory '$2' is not found.${C_OFF}" + projects_list=$(keep_prefixed_projects "$2") || (echo -e "$error_msg" && exit 1) || exit 1 + projects_indexes=( ${projects_list} ) + fi fi + # Finally select the desired command case $1 in "init") From c3180f2a06d4eda1387c0e5f1ca10d3502d66a74 Mon Sep 17 00:00:00 2001 From: Fabien Dubosson Date: Sun, 14 Jun 2015 18:08:20 +0200 Subject: [PATCH 08/18] Cache config files parsing to speedup execution --- CHANGELOG | 4 +++- TODO | 1 - src/gws | 48 +++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 46 insertions(+), 7 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 33a7573..f042edf 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,7 +1,9 @@ 0.1.8 ===== -* A second argument can be given to limit to a specific subfolder of the workspace +* A second argument can be given to limit the command to a specific + subfolder of the workspace +* Parsing of the config files is cached to speedup the execution 0.1.6 ===== diff --git a/TODO b/TODO index ac63381..7efa370 100644 --- a/TODO +++ b/TODO @@ -1,2 +1 @@ * Check command: ignore recursively folder containing a workspace (.projects.gws) -* Cache based on config file hashsum to avoid parsing each time diff --git a/src/gws b/src/gws index 75e0793..948ad77 100755 --- a/src/gws +++ b/src/gws @@ -61,6 +61,9 @@ PROJECTS_FILE=".projects.gws" # Name of the file containing the ignored patterns IGNORE_FILE=".ignore.gws" +# Name of the file containing the cache +CACHE_FILE=".cache.gws" + # Field separator in the projects list FIELD_SEP='|' @@ -282,6 +285,17 @@ function read_projects() # Extract sorted index of projects readarray -t projects_indexes < <(for a in "${!projects[@]}"; do echo "$a"; done | sort) + # Cache the result + if [[ ! ${#projects[@]} -eq 0 ]]; then + CACHED_PROJECTS_HASH=$(md5sum "${PROJECTS_FILE}") + sed -i '/^declare -A projects=/d' "${CACHE_FILE}" + sed -i '/^declare -a projects_indexes=/d' "${CACHE_FILE}" + sed -i '/^declare -- CACHED_PROJECTS_HASH=/d' "${CACHE_FILE}" + declare -p projects >> "${CACHE_FILE}" + declare -p projects_indexes >> "${CACHE_FILE}" + declare -p CACHED_PROJECTS_HASH >> "${CACHE_FILE}" + fi + return 0 } @@ -307,6 +321,15 @@ function read_ignored() ignored_patterns+=( "$pattern" ) done < <(grep -v "^#\|^$" $IGNORE_FILE) + # Cache the result + if [[ ! ${#ignored_patterns[@]} -eq 0 ]]; then + CACHED_IGNORE_HASH=$(md5sum "${IGNORE_FILE}") + sed -i '/^declare -a ignored_patterns=/d' "${CACHE_FILE}" + sed -i '/^declare -- CACHED_IGNORE_HASH=/d' "${CACHE_FILE}" + declare -p ignored_patterns >> "${CACHE_FILE}" + declare -p CACHED_IGNORE_HASH >> "${CACHE_FILE}" + fi + return 0 } @@ -868,12 +891,27 @@ if [[ "$1" != "init" ]]; then cd .. done - # Then read the list of projects and ignore list - read_projects - read_ignored + # Read the cache if existing, create it if not existing. + [[ -e "${CACHE_FILE}" ]] && source "${CACHE_FILE}" || touch "${CACHE_FILE}" - # Remove ignored projects - projects_indexes=( $(remove_matching projects_indexes[@] ignored_patterns[@]) ) + # Parse the projects file if it is not cached or if it was updated + if [[ "$CACHED_PROJECTS_HASH" != "$(md5sum ${PROJECTS_FILE})" ]]; then + read_projects + cache_corrupt=1 + fi + + # Parse the ignore file if it is not cached or if it was updated + if [[ -e "${IGNORE_FILE}" ]] && [[ "$CACHED_IGNORE_HASH" != "$(md5sum ${IGNORE_FILE})" ]]; then + read_ignored + cache_corrupt=1 + fi + + # Update projects index if cache is corrupted + if [[ -n "$cache_corrupt" ]]; then + projects_indexes=( $(remove_matching projects_indexes[@] ignored_patterns[@]) ) + sed -i '/^declare -a projects_indexes=/d' "${CACHE_FILE}" + declare -p projects_indexes >> "${CACHE_FILE}" + fi # If a path is specified as second argument, limit projects to the ones matching # the path From 4dc77ecda4ab0146fd9d7bca60c8c91b0eb09def Mon Sep 17 00:00:00 2001 From: Fabien Dubosson Date: Sun, 14 Jun 2015 22:37:56 +0200 Subject: [PATCH 09/18] Multiple remotes implemented --- src/gws | 117 ++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 85 insertions(+), 32 deletions(-) diff --git a/src/gws b/src/gws index d1b1f9e..1745e6a 100755 --- a/src/gws +++ b/src/gws @@ -67,6 +67,9 @@ CACHE_FILE=".cache.gws" # Field separator in the projects list FIELD_SEP='|' +# Array lines separator +ARRAY_LINE_SEP=', ' + # Separator between the URL and its name in config file URL_NAME_SEP=' ' @@ -251,7 +254,7 @@ function is_project_root() function add_project() { # Add the project to the list - projects[$1]="$2${FIELD_SEP}$3" + projects[$1]="$2" return 0 } @@ -265,7 +268,7 @@ function exists_project() # Read the list of projects from the projects list file function read_projects() { - local line dir remotes count repo upstream + local line dir remotes count repo upstream remotes_list # Read line by line (discard comments and empty lines) while read line @@ -280,6 +283,7 @@ function read_projects() # We get all the remotes count=0 + remotes_list="" while [ -n "$remotes" ]; do count=$((count + 1)) @@ -304,18 +308,15 @@ function read_projects() exit 1 fi fi - done - # Read the line fields - dir=$(cut -d${FIELD_SEP} -f1 <<< "$line" | tr -d '[[:space:]]') - repo=$(cut -d${FIELD_SEP} -f2 <<< "$line" | tr -d '[[:space:]]') - upstream=$(cut -d${FIELD_SEP} -f3 <<< "$line" | tr -d '[[:space:]]') + remotes_list+="${remote_name}${FIELD_SEP}${remote_url}${ARRAY_LINE_SEP}" + done # Skip if the dir is empty - [ -z "$dir" ] && continue + [ -z "${dir}" ] && continue # Otherwise add the project to the list - add_project "$dir" "$repo" "$upstream" + add_project "${dir}" "${remotes_list}" done < <(grep -v "^#\|^$" $PROJECTS_FILE) # Extract sorted index of projects @@ -372,14 +373,25 @@ function read_ignored() # Get the repo url from associative array values function get_repo_url() { - cut -d${FIELD_SEP} -f1 <<< "$1" - return 0 -} + local remote remote_name remote_url + declare -A assoc + + # Read the projects info + IFS=${ARRAY_LINE_SEP} read -a array <<< "$1" + + # Check if origin is present + for remote in "${array[@]}"; + do + remote_name=$(cut -d${FIELD_SEP} -f1 <<< ${remote}) + remote_url=$(cut -d${FIELD_SEP} -f2 <<< ${remote}) + assoc["${remote_name}"]="${remote_url}" + done + + [ ${assoc[${GIT_ORIGIN}]+isset} ] || return 1 + + # Return the URL + cut -d${FIELD_SEP} -f2 <<< ${array[${GIT_ORIGIN}]} -# Get the upstream url from associative array values -function get_upstream_url() -{ - cut -d${FIELD_SEP} -f2 <<< "$1" return 0 } @@ -482,7 +494,7 @@ function git_add_remote() } # Get a remote url -function git_remote() +function git_remote_url() { local cmd @@ -495,6 +507,34 @@ function git_remote() return 0 } +# Get the list of remotes +function git_remotes() +{ + local cmd + + # Git command to execute + cmd=( "git" "remote" ) + + # Run the command and print the output + (cd "$1" && "${cmd[@]}") + + return 0 +} + +# Check if a given remote name exists +function git_remote_exists() +{ + local cmd + + # Git command to execute + cmd=( "git" "remote" ) + + # Run the command + (cd "$1" && "${cmd[@]}" | grep "^$2\$") > /dev/null 2>&1 + + return $? +} + # Get the current branch name function git_branch() { @@ -611,7 +651,7 @@ function cmd_init() # Check if already a workspace [[ -f ${PROJECTS_FILE} ]] && echo -e "${C_RED}Already a workspace.${C_OFF}" && return 1 - local found + local found remote declare -a found # Prepare the list of all existing projects, sorted @@ -621,7 +661,12 @@ function cmd_init() # Create the list of repositories output=$(for dir in "${found[@]}" do - echo "$dir | $(git_remote $dir "${GIT_ORIGIN}") | $(git_remote $dir "${GIT_UPSTREAM}")" + echo -n "$dir | $(git_remote_url $dir "${GIT_ORIGIN}")" + for remote in $(git_remotes "$dir"); + do + [[ "$remote" != "${GIT_ORIGIN}" ]] && echo -n " | $(git_remote_url $dir $remote) $remote" + done + echo done) # Write the file if it is not empty @@ -635,14 +680,13 @@ function cmd_init() # Update command function cmd_update() { - local dir repo upstream + local dir repo upstream remote remote_name remote_url # For all projects for dir in "${projects_indexes[@]}" do # Get informations about the current project repo=$(get_repo_url "${projects[$dir]}") - upstream=$(get_upstream_url "${projects[$dir]}") # Print the repository echo -e "${C_BLUE}$dir${C_OFF}:" @@ -663,21 +707,30 @@ function cmd_update() printf "\n" # Next repository if already existing - [[ -d "$dir" ]] && continue + if [[ ! -d "$dir" ]]; then - # Print the information - printf "${INDENT}%-${MBL}s${C_CYAN} %s${C_OFF}\n" " " "Cloning…" + # Print the information + printf "${INDENT}%-${MBL}s${C_CYAN} %s${C_OFF}\n" " " "Cloning…" - # Clone the repository - if ! git_clone "$repo" "$dir"; then - printf "${INDENT}%-${MBL}s${C_RED} %s${C_OFF}\n" " " "Error" - return 1 - fi + # Clone the repository + if ! git_clone "$repo" "$dir"; then + printf "${INDENT}%-${MBL}s${C_RED} %s${C_OFF}\n" " " "Error" + return 1 + fi - # If an upstream url is set, add it - [[ ! -z "$upstream" ]] && git_add_remote "$dir" "$GIT_UPSTREAM" "$upstream" + printf "${INDENT}%-${MBL}s${C_GREEN} %s${C_OFF}\n" " " "Cloned" + fi - printf "${INDENT}%-${MBL}s${C_GREEN} %s${C_OFF}\n" " " "Cloned" + # Verify all remotes, create if not existing + IFS=${ARRAY_LINE_SEP} read -a array <<< "${projects[$dir]}" + for remote in "${array[@]}" + do + remote_name=$(cut -d${FIELD_SEP} -f1 <<< ${remote}) + remote_url=$(cut -d${FIELD_SEP} -f2 <<< ${remote}) + if ! git_remote_exists "${dir}" "${remote_name}"; then + git_add_remote "${dir}" "${remote_name}" "${remote_url}" + fi + done done return 0 From 124713447f0112c2d018125574a638f6e12f9537 Mon Sep 17 00:00:00 2001 From: Fabien Dubosson Date: Sun, 14 Jun 2015 22:38:28 +0200 Subject: [PATCH 10/18] Add multiple remote to test --- tests/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/Makefile b/tests/Makefile index 4bbac74..2bee350 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -18,6 +18,8 @@ $(WORKSPACE): cd $(WORKSPACE)/work/docker-gitlab; git co gh-pages git clone https://github.com/harelba/q $(WORKSPACE)/tools/q cd $(WORKSPACE)/tools/q; git co gh-pages + cd $(WORKSPACE)/tools/q; git remote add myone http://coool + cd $(WORKSPACE)/tools/q; git remote add upstream testurl git clone https://github.com/buildinspace/peru $(WORKSPACE)/tools/peru git clone https://github.com/dgorissen/coursera-dl $(WORKSPACE)/tools/coursera-dl cd $(WORKSPACE)/tools/coursera-dl; git co master From 36c2b52ddd5186e98c86e3a1ef3c1720fa79edea Mon Sep 17 00:00:00 2001 From: Fabien Dubosson Date: Sun, 14 Jun 2015 23:42:50 +0200 Subject: [PATCH 11/18] Change way to access repository path Prevent trying to access a non-existing folder to get its path. --- src/gws | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gws b/src/gws index 1745e6a..d31c345 100755 --- a/src/gws +++ b/src/gws @@ -223,7 +223,7 @@ function keep_prefixed_projects() for dir in "${projects_indexes[@]}" do # Get its full path - current=$(cd $dir; pwd)/ + current="${PWD}/${dir}/" # If it match, add it to the output [[ $current =~ ^$limit_to ]] && echo -n "$dir " From 4d33c19204ce45eb5b0d666e9556aa56eea9f6c9 Mon Sep 17 00:00:00 2001 From: Fabien Dubosson Date: Sun, 14 Jun 2015 23:46:29 +0200 Subject: [PATCH 12/18] Correct bug with cache (non-invalidate when needed) --- src/gws | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/src/gws b/src/gws index d31c345..c2cbdf4 100755 --- a/src/gws +++ b/src/gws @@ -268,6 +268,17 @@ function exists_project() # Read the list of projects from the projects list file function read_projects() { + # Store cache state + CACHED_PROJECTS_HASH=$(md5sum "${PROJECTS_FILE}" 2>/dev/null || echo NONE) + sed -i '/^declare -- CACHED_PROJECTS_HASH=/d' "${CACHE_FILE}" + declare -p CACHED_PROJECTS_HASH >> "${CACHE_FILE}" + + # Clear previous cache state + sed -i '/^declare -A projects=/d' "${CACHE_FILE}" + sed -i '/^declare -a projects_indexes=/d' "${CACHE_FILE}" + projects=() + projects_indexes=() + local line dir remotes count repo upstream remotes_list # Read line by line (discard comments and empty lines) @@ -324,13 +335,8 @@ function read_projects() # Cache the result if [[ ! ${#projects[@]} -eq 0 ]]; then - CACHED_PROJECTS_HASH=$(md5sum "${PROJECTS_FILE}") - sed -i '/^declare -A projects=/d' "${CACHE_FILE}" - sed -i '/^declare -a projects_indexes=/d' "${CACHE_FILE}" - sed -i '/^declare -- CACHED_PROJECTS_HASH=/d' "${CACHE_FILE}" declare -p projects >> "${CACHE_FILE}" declare -p projects_indexes >> "${CACHE_FILE}" - declare -p CACHED_PROJECTS_HASH >> "${CACHE_FILE}" fi return 0 @@ -339,6 +345,15 @@ function read_projects() # Read the list of ignored patterns from the file function read_ignored() { + # Store cache state + CACHED_IGNORE_HASH=$(md5sum "${IGNORE_FILE}" 2>/dev/null || echo NONE) + sed -i '/^declare -- CACHED_IGNORE_HASH=/d' "${CACHE_FILE}" + declare -p CACHED_IGNORE_HASH >> "${CACHE_FILE}" + + # Remove previous version in cache + sed -i '/^declare -a ignored_patterns=/d' "${CACHE_FILE}" + ignored_patterns=() + [[ -e "$IGNORE_FILE" ]] || return 0 local pattern @@ -358,13 +373,9 @@ function read_ignored() ignored_patterns+=( "$pattern" ) done < <(grep -v "^#\|^$" $IGNORE_FILE) - # Cache the result + # Cache the results if [[ ! ${#ignored_patterns[@]} -eq 0 ]]; then - CACHED_IGNORE_HASH=$(md5sum "${IGNORE_FILE}") - sed -i '/^declare -a ignored_patterns=/d' "${CACHE_FILE}" - sed -i '/^declare -- CACHED_IGNORE_HASH=/d' "${CACHE_FILE}" declare -p ignored_patterns >> "${CACHE_FILE}" - declare -p CACHED_IGNORE_HASH >> "${CACHE_FILE}" fi return 0 @@ -984,19 +995,11 @@ if [[ "$1" != "init" ]]; then [[ -e "${CACHE_FILE}" ]] && source "${CACHE_FILE}" || touch "${CACHE_FILE}" # Parse the projects file if it is not cached or if it was updated - if [[ "$CACHED_PROJECTS_HASH" != "$(md5sum ${PROJECTS_FILE})" ]]; then + if [[ "$CACHED_PROJECTS_HASH" != "$(md5sum ${PROJECTS_FILE} 2>/dev/null || echo NONE)" ]] || + [[ "$CACHED_IGNORE_HASH" != "$(md5sum ${IGNORE_FILE} 2>/dev/null || echo NONE)" ]]; then read_projects - cache_corrupt=1 - fi - - # Parse the ignore file if it is not cached or if it was updated - if [[ -e "${IGNORE_FILE}" ]] && [[ "$CACHED_IGNORE_HASH" != "$(md5sum ${IGNORE_FILE})" ]]; then read_ignored - cache_corrupt=1 - fi - # Update projects index if cache is corrupted - if [[ -n "$cache_corrupt" ]]; then projects_indexes=( $(remove_matching projects_indexes[@] ignored_patterns[@]) ) sed -i '/^declare -a projects_indexes=/d' "${CACHE_FILE}" declare -p projects_indexes >> "${CACHE_FILE}" From 696e066335de07eea25f803c71a20b02045392c6 Mon Sep 17 00:00:00 2001 From: Fabien Dubosson Date: Thu, 25 Jun 2015 10:53:46 +0200 Subject: [PATCH 13/18] Correct some shellsheck warnings --- src/gws | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/gws b/src/gws index c2cbdf4..4e2aac3 100755 --- a/src/gws +++ b/src/gws @@ -279,14 +279,14 @@ function read_projects() projects=() projects_indexes=() - local line dir remotes count repo upstream remotes_list + local line dir remotes count repo remotes_list # Read line by line (discard comments and empty lines) while read line do # Remove inline comments line=$(sed -e 's/#.*$//' <<< "$line") - + # We get the directory dir=$(cut -d${FIELD_SEP} -f1 <<< "$line" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') # We get the rest of the configuration line containing remotes @@ -398,7 +398,7 @@ function get_repo_url() assoc["${remote_name}"]="${remote_url}" done - [ ${assoc[${GIT_ORIGIN}]+isset} ] || return 1 + [ "${assoc[${GIT_ORIGIN}]+isset}" ] || return 1 # Return the URL cut -d${FIELD_SEP} -f2 <<< ${array[${GIT_ORIGIN}]} @@ -672,7 +672,7 @@ function cmd_init() # Create the list of repositories output=$(for dir in "${found[@]}" do - echo -n "$dir | $(git_remote_url $dir "${GIT_ORIGIN}")" + echo -n "$dir | $(git_remote_url "$dir" "${GIT_ORIGIN}")" for remote in $(git_remotes "$dir"); do [[ "$remote" != "${GIT_ORIGIN}" ]] && echo -n " | $(git_remote_url $dir $remote) $remote" @@ -691,7 +691,7 @@ function cmd_init() # Update command function cmd_update() { - local dir repo upstream remote remote_name remote_url + local dir repo remote remote_name remote_url # For all projects for dir in "${projects_indexes[@]}" @@ -718,7 +718,7 @@ function cmd_update() printf "\n" # Next repository if already existing - if [[ ! -d "$dir" ]]; then + if [[ ! -d "$dir" ]]; then # Print the information printf "${INDENT}%-${MBL}s${C_CYAN} %s${C_OFF}\n" " " "Cloning…" @@ -750,7 +750,7 @@ function cmd_update() # Status command function cmd_status() { - local dir repo upstream branch branch_done rc uptodate printed + local dir repo branch branch_done rc uptodate printed uptodate=1 @@ -766,7 +766,8 @@ function cmd_status() # Check if repository already exists, and continue if it is not the case if [ ! -d "$dir" ]; then printf "${INDENT}%-${MBL}s${C_YELLOW} %s${C_OFF} " " " "Missing repository" - [[ -z $repo ]] && echo -e "${C_BLUE}[Local only repository]${C_OFF}\n" || printf "\n" + [[ -z $repo ]] && echo -e "${C_BLUE}[Local only repository]${C_OFF}" + printf "\n" uptodate=0 continue fi @@ -947,7 +948,7 @@ function cmd_check() fi # Check if it is listed as project and print according message - if exists_project $dir; then + if exists_project "$dir"; then printf "${INDENT}%-${MBL}s${C_GREEN} %s${C_OFF}\n" " " "Known" else printf "${INDENT}%-${MBL}s${C_RED} %s${C_OFF}\n" " " "Unknown" @@ -992,7 +993,7 @@ if [[ "$1" != "init" ]]; then done # Read the cache if existing, create it if not existing. - [[ -e "${CACHE_FILE}" ]] && source "${CACHE_FILE}" || touch "${CACHE_FILE}" + ([[ -e "${CACHE_FILE}" ]] && source "${CACHE_FILE}") || touch "${CACHE_FILE}" # Parse the projects file if it is not cached or if it was updated if [[ "$CACHED_PROJECTS_HASH" != "$(md5sum ${PROJECTS_FILE} 2>/dev/null || echo NONE)" ]] || From 38202435ef204af5eb115b867bf2cbfad97ace6a Mon Sep 17 00:00:00 2001 From: Fabien Dubosson Date: Mon, 20 Jul 2015 21:40:46 +0200 Subject: [PATCH 14/18] Correct bug. Was sourcing in subshell --- src/gws | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gws b/src/gws index 4e2aac3..1000a35 100755 --- a/src/gws +++ b/src/gws @@ -993,9 +993,9 @@ if [[ "$1" != "init" ]]; then done # Read the cache if existing, create it if not existing. - ([[ -e "${CACHE_FILE}" ]] && source "${CACHE_FILE}") || touch "${CACHE_FILE}" + touch "${CACHE_FILE}" + [[ -e "${CACHE_FILE}" ]] && source "${CACHE_FILE}" - # Parse the projects file if it is not cached or if it was updated if [[ "$CACHED_PROJECTS_HASH" != "$(md5sum ${PROJECTS_FILE} 2>/dev/null || echo NONE)" ]] || [[ "$CACHED_IGNORE_HASH" != "$(md5sum ${IGNORE_FILE} 2>/dev/null || echo NONE)" ]]; then read_projects From 8dc01ac1c30bd13f1783aadc780ea060792c0b7c Mon Sep 17 00:00:00 2001 From: Fabien Dubosson Date: Mon, 20 Jul 2015 22:30:12 +0200 Subject: [PATCH 15/18] Adapt zsh completion for files and commands --- completions/zsh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/completions/zsh b/completions/zsh index 0b148af..afedee9 100644 --- a/completions/zsh +++ b/completions/zsh @@ -1,4 +1,8 @@ #compdef gws # Zsh completion for gws -_arguments "1:commands:(init update status fetch ff check)" "2:filenames:_files" +_path_or_command(){ + _alternative 'cmds:commands:(init update status fetch ff check)' 'files:filenames:_path_files -/' +} + +_arguments "1::path or command:_path_or_command" "2::filenames:_path_files -/" From 6431bb6ac37f4e6129ea388e84550d9b834c0ebb Mon Sep 17 00:00:00 2001 From: Fabien Dubosson Date: Mon, 20 Jul 2015 22:49:36 +0200 Subject: [PATCH 16/18] Update bash completion for files and commands --- completions/bash | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/completions/bash b/completions/bash index 7fefcdf..656f183 100644 --- a/completions/bash +++ b/completions/bash @@ -5,11 +5,13 @@ _gws() local cur="${COMP_WORDS[COMP_CWORD]}" case $COMP_CWORD in - 1) COMPREPLY=( $(compgen -W "init update status fetch ff check" -- $cur) ) + 1) COMPREPLY=( $(compgen -S " " -W "init update status fetch ff check" -- $cur) $(compgen -S "/" -A directory -- "$cur") ) ;; - 2) COMPREPLY=( $(compgen -f "$cur") ) + 2) COMPREPLY=( $(compgen -S "/" -A directory -- "$cur") ) ;; esac + + compopt -o nospace; } complete -F _gws gws From 485b192341f8afad874b5d0cc348a70921a813f4 Mon Sep 17 00:00:00 2001 From: Fabien Dubosson Date: Mon, 20 Jul 2015 23:14:10 +0200 Subject: [PATCH 17/18] Update README.md --- README.md | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 78d909e..3c4525c 100644 --- a/README.md +++ b/README.md @@ -35,19 +35,18 @@ Installation * You can put it directly in `/bin` as root user, but it is complicated to keep it up-to-date. - * Otherwise, it is also possible to put it in your home folder, for example - in `~/.local/bin`. But you have to be sure that this folder is in your - `$PATH`. (I should also suggest you to have a look at - [peru](https://github.com/buildinspace/peru) which permits to keep files - from different sources up to date with one command). - - * For `bash` you can include any directory on your `$PATH` by - including `export PATH="$PATH:/path/to/scripts/dir"` in your - `~/.bashrc` file. + * It is also possible to put it in your home folder, for example in + `~/.local/bin`. You have to be sure that this folder is in your `$PATH`. For + `bash` you can include any directory on your `$PATH` by including `export + PATH="$PATH:/path/to/scripts/dir"` in your `~/.bashrc` file. * On Mac OS X, it may be necessary to upgrade bash to have a version `> 4.0`. It could be done with: `brew install bash`. +On a side note, I could also suggest you to have a look at +[peru](https://github.com/buildinspace/peru) which permits to keep files from +different sources up to date with one command. + QuickStart ---------- @@ -209,27 +208,27 @@ Syntaxes One project per line. Must be of the form: - any/folder/name | url + | [ | [ | ... ]] + +knowing that: -or +* The `` can be skipped and `origin` will be used instead - any/folder/name | url | upstream_url +* The `` can be skipped and `upstream` will be used instead -knowing that: +* There must be at least one `` mapping to `origin` * There can also be blank lines, comments or inline comments. Comments start with `#` until the end of the line. -* The *name* can be any valid linux folder name not containing `|` or `#`. - -* The *urls* are passed to git as-is, so can be anything accepted by git, but - must not contain `|` or `#`. For instance if you have SSH aliases in your - config they are accepted. +* The *folder paths* can be any valid linux folder path not containing `|`, `#` + or spaces. -* The *url* will be used for cloning the repository, thus mapped to the - `origin` remote. +* The *remote names* can be any string not containing `|`, `#` or spaces. -* The *upstream_url* will be mapped to the `upstream` remote in git. +* The *remote urls* are passed to git as-is, so can be anything accepted by git, + but must not contain `|`, `#` or spaces. For instance if you have SSH aliases + in your config they are accepted. ### .ignore.gws From 32a0c8d2dfb54c0151591475b7b1dd12d1d041b8 Mon Sep 17 00:00:00 2001 From: Fabien Dubosson Date: Mon, 20 Jul 2015 23:17:20 +0200 Subject: [PATCH 18/18] Bump version number to 0.1.8 --- src/gws | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gws b/src/gws index 1000a35..6eeba83 100755 --- a/src/gws +++ b/src/gws @@ -5,7 +5,7 @@ # OS: Probably all linux distributions # # Requirements: git, bash > 4.0 # # License: MIT (See below) # -# Version: 0.1.8-DEV # +# Version: 0.1.8 # # # # 'gws' is the abbreviation of 'Git WorkSpace'. # # This is an helper to manage workspaces which contain git repositories. # @@ -50,7 +50,7 @@ set -o pipefail # {{{ Parameters # Version number -VERSION="0.1.8-DEV" +VERSION="0.1.8" # Starting directory START_PWD="$(pwd)"