Skip to content

Commit 1e29659

Browse files
authored
Merge pull request #1 from jmylchreest/main
Support for multiple paths
2 parents ef116cc + 87a5305 commit 1e29659

File tree

2 files changed

+207
-51
lines changed

2 files changed

+207
-51
lines changed

README.md

+21-1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,14 @@ zgenom load empresslabs/gitprofiles.plugin.zsh
4949
zplug empresslabs/gitprofiles.plugin.zsh
5050
```
5151

52+
#### [antidote](https://github.com/mattmc3/antidote.git)
53+
54+
Add the following to your .zsh_plugins.txt file for antidote:
55+
56+
```shell
57+
empresslabs/gitprofiles.plugin.zsh
58+
```
59+
5260
## Usage
5361

5462
#### Define where your profiles are stored
@@ -73,5 +81,17 @@ zstyle ":empresslabs:git:profile" path "$HOME/.config/git/profiles"
7381
name = Bruno Sales
7482
7583
# signingkey = 1234567890
76-
path = "/home/baliestri/work"
84+
paths = "/home/baliestri/work"
85+
86+
[profile "personal"]
87+
name = Bruno Sales
88+
89+
# signingkey = 1234567890
90+
paths = "~/personal",
91+
"~/src/personal/*",
92+
"~/src/mytopsecretproject"
7793
```
94+
95+
Multiple paths can be defined for a profile, separated by either newline or commas. The paths are processed in the order they are defined, with exact matches taking precedence over wild card matches. Tildes are expanded to ${HOME}.
96+
97+
It is possible to get debug information by setting the `GP_DEBUG` environment variable to any value within your current session.

gitprofiles.plugin.zsh

+186-50
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,26 @@
11
# Copyright (c) Bruno Sales <[email protected]>. Licensed under the MIT License.
22
# See the LICENSE file in the project root for full license information.
33

4+
# vim: set ts=4 sw=4 tw=0 et :
45
#!/usr/bin/env zsh
56

7+
function __is_git_repo() {
8+
# if git isnt installes, this will also return false
9+
if [[ -n "$(git rev-parse --is-inside-work-tree 2>/dev/null)" ]]; then
10+
return 0
11+
else
12+
return 1
13+
fi
14+
}
15+
616
function __gitprofiles_hook() {
7-
## Check if git is installed
8-
if (( ! $+commands[git] )); then
17+
# make sure we're in a git repo
18+
if ! __is_git_repo; then
919
return 1
1020
fi
1121

12-
local -A profile_path_map=()
13-
local -A profile_cfg_map=()
22+
typeset -A profile_paths_map
23+
typeset -A profile_cfg_map
1424

1525
## Get the path to the profile file
1626
zstyle -s ":empresslabs:git:profile" path profile_filepath
@@ -20,70 +30,196 @@ function __gitprofiles_hook() {
2030
return 1
2131
fi
2232

33+
# Ensure glob patterns that don't match don't cause errors
34+
setopt LOCAL_OPTIONS NO_NOMATCH
35+
2336
## Load all stored profiles
24-
local profiles=($(grep -o '\[profile [^]]*\]' ${profile_filepath} | tr -d '[]" ' | sed 's/profile//g' | tr '\n' ' '))
37+
local profiles=()
38+
local current_section=""
39+
40+
while IFS= read -r line; do
41+
# Skip empty lines and comments
42+
[[ -z "$line" || "$line" == \#* ]] && continue
43+
44+
# Check for profile section
45+
if [[ "$line" =~ '^\[profile[[:space:]]+"([^"]+)"\]' ]]; then
46+
current_section="${match[1]}"
47+
profiles+=("$current_section")
48+
# [[ -n "${GP_DEBUG}" ]] && print -u2 "Found profile: ${current_section}"
49+
fi
50+
done < "${profile_filepath}"
2551

2652
## Check if default profile exists
27-
if [[ ! "${profiles}" =~ "default" ]]; then
28-
echo "gitprofiles: 'default' profile not found in '${profile_filepath}'"
53+
if [[ ! "${profiles[(r)default]}" ]]; then
54+
print -u2 "gitprofiles: 'default' profile not found in '${profile_filepath}'"
2955
return 1
3056
fi
3157

32-
## Iterate over all profiles to get the name, email, signingkey and path
58+
# Function to parse paths into an array and support tilde expansion
59+
function __parse_paths() {
60+
local raw_paths="$1"
61+
# [[ -n "${GP_DEBUG}" ]] && print -u2 "Raw paths input: ${(q)raw_paths}"
62+
63+
local -a path_lines
64+
# split on commas or newlines
65+
path_lines=(${(s:,:)${(f)raw_paths}})
66+
67+
# Process each line
68+
local -a paths
69+
local line
70+
for line in $path_lines; do
71+
# remove newlines
72+
line="${line//'\n'/}"
73+
74+
# remove quotes
75+
line="${line//[\"\']}"
76+
77+
# Remove trailing commas
78+
line="${line%%,}"
79+
80+
# Trim whitespace
81+
line="${line## }"
82+
line="${line%% }"
83+
84+
# Expand tildes
85+
if [[ $line = "~"* ]]; then
86+
line=${HOME}${line#"~"}
87+
fi
88+
89+
# Skip empty lines
90+
[[ -n "$line" ]] && paths+=("$line")
91+
done
92+
93+
# Expand tildes
94+
# paths=(${~paths}) # this doesnt work as it expands the glob
95+
96+
# [[ -n "${GP_DEBUG}" ]] && print -u2 "Final paths: ${paths}"
97+
print -l -- ${paths}
98+
}
99+
100+
## Parse configuration for each profile
33101
for profile in ${profiles}; do
34-
local -A profile_value_map=()
35-
36-
while read -r key value; do
37-
case "${key}" in
38-
name)
39-
profile_value_map[name]="${value}"
40-
;;
41-
email)
42-
profile_value_map[email]="${value}"
43-
;;
44-
signingkey)
45-
profile_value_map[signingkey]="${value}"
46-
;;
47-
path)
48-
profile_value_map[path]="${value}"
49-
;;
50-
esac
51-
done < <(awk -F ' = ' '/^\[profile/{p=0} /^\[profile "[^"]*'"${profile}"'"/{p=1} p {gsub(/"/, "", $2); print $1,$2}' ${profile_filepath})
52-
53-
profile_path_map[${profile}]="${profile_value_map[path]}"
54-
55-
profile_cfg_map[${profile}.name]="${profile_value_map[name]}"
56-
profile_cfg_map[${profile}.email]="${profile_value_map[email]}"
57-
58-
if [[ -n "${profile[signingkey]}" ]]; then
59-
profile_cfg_map[${profile}.signingkey]="${profile_value_map[signingkey]}"
102+
typeset -A profile_value_map
103+
local in_current_profile=0
104+
local in_paths=0
105+
local paths_tmp=()
106+
107+
while IFS= read -r line; do
108+
# Skip empty lines and comments
109+
[[ -z "$line" || "$line" == \#* ]] && continue
110+
111+
# Check for profile section
112+
if [[ "$line" =~ '^\[profile[[:space:]]+"([^"]+)"\]' ]]; then
113+
if [[ "${match[1]}" == "$profile" ]]; then
114+
in_current_profile=1
115+
else
116+
in_current_profile=0
117+
in_paths=0
118+
fi
119+
continue
120+
fi
121+
122+
# Only process lines for current profile
123+
(( in_current_profile )) || continue
124+
125+
# Parse key-value pairs
126+
if [[ "$line" =~ '^[[:space:]]*([^=]+)[[:space:]]*=[[:space:]]*(.*)' ]]; then
127+
local key="${match[1]## }" # Trim leading spaces
128+
key="${key%% }" # Trim trailing spaces
129+
local value="${match[2]}" # Keep spaces in value for now
130+
131+
case "$key" in
132+
name|email|signingkey)
133+
# Remove quotes and trim for non-path values
134+
value="${value## }" # Trim leading spaces
135+
value="${value%% }" # Trim trailing spaces
136+
value="${value#[\"\']}" # Remove leading quote
137+
value="${value%[\"\']}" # Remove trailing quote
138+
profile_value_map[$key]="$value"
139+
;;
140+
path|paths)
141+
in_paths=1
142+
paths_tmp=("$value")
143+
;;
144+
esac
145+
elif (( in_paths )) && [[ "$line" =~ '^[[:space:]]+(.*)' ]]; then
146+
# Handle indented continuation lines for paths
147+
local value="${match[1]}"
148+
paths_tmp+=("$value")
149+
fi
150+
done < "${profile_filepath}"
151+
152+
# Join and parse paths
153+
if (( ${#paths_tmp} > 0 )); then
154+
local joined_paths="${(j:\n:)paths_tmp}"
155+
profile_paths_map[$profile]="${(@f)$(__parse_paths "$joined_paths")}"
60156
fi
61-
done
62157

63-
## Get the current directory
64-
local -A current=()
65-
current[dir]=$(pwd)
158+
# Store other configurations
159+
profile_cfg_map[$profile.name]="${profile_value_map[name]}"
160+
profile_cfg_map[$profile.email]="${profile_value_map[email]}"
66161

67-
## Check if the current directory is in one of the profiles paths
68-
for profile in ${(k)profile_path_map}; do
69-
if [[ "${current[dir]}" =~ "${profile_path_map[${profile}]}" ]]; then
70-
local current[profile]="${profile}"
71-
break
162+
if [[ -n "${profile_value_map[signingkey]}" ]]; then
163+
profile_cfg_map[$profile.signingkey]="${profile_value_map[signingkey]}"
72164
fi
73165
done
74166

75-
## If the current directory is not in any profile path, use the default profile
76-
if [[ -z "${current[profile]}" ]]; then
77-
local current[profile]="default"
167+
# Get current directory
168+
local current_dir=$(pwd)
169+
local matched_profile="default"
170+
171+
[[ -n "${GP_DEBUG}" ]] && print -u2 "Current directory: ${current_dir}"
172+
173+
# First pass: Check for exact matches
174+
for profile in ${(k)profile_paths_map}; do
175+
[[ -n "${GP_DEBUG}" ]] && print -u2 "Testing Profile (exact): ${profile}"
176+
177+
local paths=(${=profile_paths_map[$profile]}) # Convert to array
178+
for path_pattern in $paths; do
179+
# Only do exact match if pattern doesn't contain a wildcard
180+
if [[ ! $path_pattern =~ '[*?]' ]] && [[ "${current_dir}" = "${path_pattern}" ]]; then
181+
[[ -n "${GP_DEBUG}" ]] && print -u2 "Matched (exact) path: ${path_pattern}"
182+
matched_profile="${profile}"
183+
break 2
184+
fi
185+
done
186+
done
187+
188+
# Second pass: Check for wildcard matches (only if no exact match found)
189+
if [[ "${matched_profile}" = "default" ]]; then
190+
for profile in ${(k)profile_paths_map}; do
191+
[[ -n "${GP_DEBUG}" ]] && print -u2 "Testing Profile (wildcard): ${profile}"
192+
193+
local paths=(${=profile_paths_map[$profile]}) # Convert to array
194+
for path_pattern in $paths; do
195+
# Only do regex match if we have a wildcard
196+
if [[ $path_pattern =~ '[*?]' ]] && [[ "${current_dir}" =~ "${path_pattern}" ]]; then
197+
[[ -n "${GP_DEBUG}" ]] && print -u2 "Matched (*) path: ${path_pattern}"
198+
matched_profile="${profile}"
199+
break 2
200+
fi
201+
done
202+
done
78203
fi
79204

80205
## Set the current profile name and email
81-
git config --global user.name "${profile_cfg_map[${current[profile]}.name]}"
82-
git config --global user.email "${profile_cfg_map[${current[profile]}.email]}"
206+
git config --global user.name "${profile_cfg_map[${matched_profile}.name]}"
207+
git config --global user.email "${profile_cfg_map[${matched_profile}.email]}"
83208

84209
## Set the current profile signingkey if it exists
85-
if [[ -n "${profile_cfg_map[${current[profile]}.signingkey]}" ]]; then
86-
git config --global user.signingkey "${profile_cfg_map[${current[profile]}.signingkey]}"
210+
if [[ -n "${profile_cfg_map[${matched_profile}.signingkey]}" ]]; then
211+
git config --global user.signingkey "${profile_cfg_map[${matched_profile}.signingkey]}"
212+
fi
213+
214+
# Print debug information if GP_DEBUG is set
215+
if [[ -n "${GP_DEBUG}" ]] && [[ -n "${matched_profile}" ]]; then
216+
print -u2 "Matched profile: ${matched_profile}"
217+
print -u2 "Using configuration:"
218+
print -u2 " name: ${profile_cfg_map[${matched_profile}.name]}"
219+
print -u2 " email: ${profile_cfg_map[${matched_profile}.email]}"
220+
if [[ -n "${profile_cfg_map[${matched_profile}.signingkey]}" ]]; then
221+
print -u2 " signingkey: ${profile_cfg_map[${matched_profile}.signingkey]}"
222+
fi
87223
fi
88224
}
89225

0 commit comments

Comments
 (0)