1
1
# Copyright (c) Bruno Sales <[email protected] >. Licensed under the MIT License.
2
2
# See the LICENSE file in the project root for full license information.
3
3
4
+ # vim: set ts=4 sw=4 tw=0 et :
4
5
#! /usr/bin/env zsh
5
6
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
+
6
16
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
9
19
return 1
10
20
fi
11
21
12
- local -A profile_path_map=()
13
- local -A profile_cfg_map=()
22
+ typeset -A profile_paths_map
23
+ typeset -A profile_cfg_map
14
24
15
25
# # Get the path to the profile file
16
26
zstyle -s " :empresslabs:git:profile" path profile_filepath
@@ -20,70 +30,196 @@ function __gitprofiles_hook() {
20
30
return 1
21
31
fi
22
32
33
+ # Ensure glob patterns that don't match don't cause errors
34
+ setopt LOCAL_OPTIONS NO_NOMATCH
35
+
23
36
# # 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} "
25
51
26
52
# # 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} '"
29
55
return 1
30
56
fi
31
57
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
33
101
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 " )} "
60
156
fi
61
- done
62
157
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]} "
66
161
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]} "
72
164
fi
73
165
done
74
166
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
78
203
fi
79
204
80
205
# # 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]} "
83
208
84
209
# # 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
87
223
fi
88
224
}
89
225
0 commit comments