]> src.twobees.de Git - dotfiles.git/blob - stow/oh-my-zsh/.oh-my-zsh/lib/git.zsh
...
[dotfiles.git] / stow / oh-my-zsh / .oh-my-zsh / lib / git.zsh
1 # The git prompt's git commands are read-only and should not interfere with
2 # other processes. This environment variable is equivalent to running with `git
3 # --no-optional-locks`, but falls back gracefully for older versions of git.
4 # See git(1) for and git-status(1) for a description of that flag.
5 #
6 # We wrap in a local function instead of exporting the variable directly in
7 # order to avoid interfering with manually-run git commands by the user.
8 function __git_prompt_git() {
9   GIT_OPTIONAL_LOCKS=0 command git "$@"
10 }
11
12 function git_prompt_info() {
13   # If we are on a folder not tracked by git, get out.
14   # Otherwise, check for hide-info at global and local repository level
15   if ! __git_prompt_git rev-parse --git-dir &> /dev/null \
16      || [[ "$(__git_prompt_git config --get oh-my-zsh.hide-info 2>/dev/null)" == 1 ]]; then
17     return 0
18   fi
19
20   local ref
21   ref=$(__git_prompt_git symbolic-ref --short HEAD 2> /dev/null) \
22   || ref=$(__git_prompt_git rev-parse --short HEAD 2> /dev/null) \
23   || return 0
24
25   # Use global ZSH_THEME_GIT_SHOW_UPSTREAM=1 for including upstream remote info
26   local upstream
27   if (( ${+ZSH_THEME_GIT_SHOW_UPSTREAM} )); then
28     upstream=$(__git_prompt_git rev-parse --abbrev-ref --symbolic-full-name "@{upstream}" 2>/dev/null) \
29     && upstream=" -> ${upstream}"
30   fi
31
32   echo "${ZSH_THEME_GIT_PROMPT_PREFIX}${ref:gs/%/%%}${upstream:gs/%/%%}$(parse_git_dirty)${ZSH_THEME_GIT_PROMPT_SUFFIX}"
33 }
34
35 # Checks if working tree is dirty
36 function parse_git_dirty() {
37   local STATUS
38   local -a FLAGS
39   FLAGS=('--porcelain')
40   if [[ "$(__git_prompt_git config --get oh-my-zsh.hide-dirty)" != "1" ]]; then
41     if [[ "${DISABLE_UNTRACKED_FILES_DIRTY:-}" == "true" ]]; then
42       FLAGS+='--untracked-files=no'
43     fi
44     case "${GIT_STATUS_IGNORE_SUBMODULES:-}" in
45       git)
46         # let git decide (this respects per-repo config in .gitmodules)
47         ;;
48       *)
49         # if unset: ignore dirty submodules
50         # other values are passed to --ignore-submodules
51         FLAGS+="--ignore-submodules=${GIT_STATUS_IGNORE_SUBMODULES:-dirty}"
52         ;;
53     esac
54     STATUS=$(__git_prompt_git status ${FLAGS} 2> /dev/null | tail -n 1)
55   fi
56   if [[ -n $STATUS ]]; then
57     echo "$ZSH_THEME_GIT_PROMPT_DIRTY"
58   else
59     echo "$ZSH_THEME_GIT_PROMPT_CLEAN"
60   fi
61 }
62
63 # Gets the difference between the local and remote branches
64 function git_remote_status() {
65     local remote ahead behind git_remote_status git_remote_status_detailed
66     remote=${$(__git_prompt_git rev-parse --verify ${hook_com[branch]}@{upstream} --symbolic-full-name 2>/dev/null)/refs\/remotes\/}
67     if [[ -n ${remote} ]]; then
68         ahead=$(__git_prompt_git rev-list ${hook_com[branch]}@{upstream}..HEAD 2>/dev/null | wc -l)
69         behind=$(__git_prompt_git rev-list HEAD..${hook_com[branch]}@{upstream} 2>/dev/null | wc -l)
70
71         if [[ $ahead -eq 0 ]] && [[ $behind -eq 0 ]]; then
72             git_remote_status="$ZSH_THEME_GIT_PROMPT_EQUAL_REMOTE"
73         elif [[ $ahead -gt 0 ]] && [[ $behind -eq 0 ]]; then
74             git_remote_status="$ZSH_THEME_GIT_PROMPT_AHEAD_REMOTE"
75             git_remote_status_detailed="$ZSH_THEME_GIT_PROMPT_AHEAD_REMOTE_COLOR$ZSH_THEME_GIT_PROMPT_AHEAD_REMOTE$((ahead))%{$reset_color%}"
76         elif [[ $behind -gt 0 ]] && [[ $ahead -eq 0 ]]; then
77             git_remote_status="$ZSH_THEME_GIT_PROMPT_BEHIND_REMOTE"
78             git_remote_status_detailed="$ZSH_THEME_GIT_PROMPT_BEHIND_REMOTE_COLOR$ZSH_THEME_GIT_PROMPT_BEHIND_REMOTE$((behind))%{$reset_color%}"
79         elif [[ $ahead -gt 0 ]] && [[ $behind -gt 0 ]]; then
80             git_remote_status="$ZSH_THEME_GIT_PROMPT_DIVERGED_REMOTE"
81             git_remote_status_detailed="$ZSH_THEME_GIT_PROMPT_AHEAD_REMOTE_COLOR$ZSH_THEME_GIT_PROMPT_AHEAD_REMOTE$((ahead))%{$reset_color%}$ZSH_THEME_GIT_PROMPT_BEHIND_REMOTE_COLOR$ZSH_THEME_GIT_PROMPT_BEHIND_REMOTE$((behind))%{$reset_color%}"
82         fi
83
84         if [[ -n $ZSH_THEME_GIT_PROMPT_REMOTE_STATUS_DETAILED ]]; then
85             git_remote_status="$ZSH_THEME_GIT_PROMPT_REMOTE_STATUS_PREFIX${remote:gs/%/%%}$git_remote_status_detailed$ZSH_THEME_GIT_PROMPT_REMOTE_STATUS_SUFFIX"
86         fi
87
88         echo $git_remote_status
89     fi
90 }
91
92 # Outputs the name of the current branch
93 # Usage example: git pull origin $(git_current_branch)
94 # Using '--quiet' with 'symbolic-ref' will not cause a fatal error (128) if
95 # it's not a symbolic ref, but in a Git repo.
96 function git_current_branch() {
97   local ref
98   ref=$(__git_prompt_git symbolic-ref --quiet HEAD 2> /dev/null)
99   local ret=$?
100   if [[ $ret != 0 ]]; then
101     [[ $ret == 128 ]] && return  # no git repo.
102     ref=$(__git_prompt_git rev-parse --short HEAD 2> /dev/null) || return
103   fi
104   echo ${ref#refs/heads/}
105 }
106
107
108 # Gets the number of commits ahead from remote
109 function git_commits_ahead() {
110   if __git_prompt_git rev-parse --git-dir &>/dev/null; then
111     local commits="$(__git_prompt_git rev-list --count @{upstream}..HEAD 2>/dev/null)"
112     if [[ -n "$commits" && "$commits" != 0 ]]; then
113       echo "$ZSH_THEME_GIT_COMMITS_AHEAD_PREFIX$commits$ZSH_THEME_GIT_COMMITS_AHEAD_SUFFIX"
114     fi
115   fi
116 }
117
118 # Gets the number of commits behind remote
119 function git_commits_behind() {
120   if __git_prompt_git rev-parse --git-dir &>/dev/null; then
121     local commits="$(__git_prompt_git rev-list --count HEAD..@{upstream} 2>/dev/null)"
122     if [[ -n "$commits" && "$commits" != 0 ]]; then
123       echo "$ZSH_THEME_GIT_COMMITS_BEHIND_PREFIX$commits$ZSH_THEME_GIT_COMMITS_BEHIND_SUFFIX"
124     fi
125   fi
126 }
127
128 # Outputs if current branch is ahead of remote
129 function git_prompt_ahead() {
130   if [[ -n "$(__git_prompt_git rev-list origin/$(git_current_branch)..HEAD 2> /dev/null)" ]]; then
131     echo "$ZSH_THEME_GIT_PROMPT_AHEAD"
132   fi
133 }
134
135 # Outputs if current branch is behind remote
136 function git_prompt_behind() {
137   if [[ -n "$(__git_prompt_git rev-list HEAD..origin/$(git_current_branch) 2> /dev/null)" ]]; then
138     echo "$ZSH_THEME_GIT_PROMPT_BEHIND"
139   fi
140 }
141
142 # Outputs if current branch exists on remote or not
143 function git_prompt_remote() {
144   if [[ -n "$(__git_prompt_git show-ref origin/$(git_current_branch) 2> /dev/null)" ]]; then
145     echo "$ZSH_THEME_GIT_PROMPT_REMOTE_EXISTS"
146   else
147     echo "$ZSH_THEME_GIT_PROMPT_REMOTE_MISSING"
148   fi
149 }
150
151 # Formats prompt string for current git commit short SHA
152 function git_prompt_short_sha() {
153   local SHA
154   SHA=$(__git_prompt_git rev-parse --short HEAD 2> /dev/null) && echo "$ZSH_THEME_GIT_PROMPT_SHA_BEFORE$SHA$ZSH_THEME_GIT_PROMPT_SHA_AFTER"
155 }
156
157 # Formats prompt string for current git commit long SHA
158 function git_prompt_long_sha() {
159   local SHA
160   SHA=$(__git_prompt_git rev-parse HEAD 2> /dev/null) && echo "$ZSH_THEME_GIT_PROMPT_SHA_BEFORE$SHA$ZSH_THEME_GIT_PROMPT_SHA_AFTER"
161 }
162
163 function git_prompt_status() {
164   [[ "$(__git_prompt_git config --get oh-my-zsh.hide-status 2>/dev/null)" = 1 ]] && return
165
166   # Maps a git status prefix to an internal constant
167   # This cannot use the prompt constants, as they may be empty
168   local -A prefix_constant_map
169   prefix_constant_map=(
170     '\?\? '     'UNTRACKED'
171     'A  '       'ADDED'
172     'M  '       'ADDED'
173     'MM '       'MODIFIED'
174     ' M '       'MODIFIED'
175     'AM '       'MODIFIED'
176     ' T '       'MODIFIED'
177     'R  '       'RENAMED'
178     ' D '       'DELETED'
179     'D  '       'DELETED'
180     'UU '       'UNMERGED'
181     'ahead'     'AHEAD'
182     'behind'    'BEHIND'
183     'diverged'  'DIVERGED'
184     'stashed'   'STASHED'
185   )
186
187   # Maps the internal constant to the prompt theme
188   local -A constant_prompt_map
189   constant_prompt_map=(
190     'UNTRACKED' "$ZSH_THEME_GIT_PROMPT_UNTRACKED"
191     'ADDED'     "$ZSH_THEME_GIT_PROMPT_ADDED"
192     'MODIFIED'  "$ZSH_THEME_GIT_PROMPT_MODIFIED"
193     'RENAMED'   "$ZSH_THEME_GIT_PROMPT_RENAMED"
194     'DELETED'   "$ZSH_THEME_GIT_PROMPT_DELETED"
195     'UNMERGED'  "$ZSH_THEME_GIT_PROMPT_UNMERGED"
196     'AHEAD'     "$ZSH_THEME_GIT_PROMPT_AHEAD"
197     'BEHIND'    "$ZSH_THEME_GIT_PROMPT_BEHIND"
198     'DIVERGED'  "$ZSH_THEME_GIT_PROMPT_DIVERGED"
199     'STASHED'   "$ZSH_THEME_GIT_PROMPT_STASHED"
200   )
201
202   # The order that the prompt displays should be added to the prompt
203   local status_constants
204   status_constants=(
205     UNTRACKED ADDED MODIFIED RENAMED DELETED
206     STASHED UNMERGED AHEAD BEHIND DIVERGED
207   )
208
209   local status_text
210   status_text="$(__git_prompt_git status --porcelain -b 2> /dev/null)"
211
212   # Don't continue on a catastrophic failure
213   if [[ $? -eq 128 ]]; then
214     return 1
215   fi
216
217   # A lookup table of each git status encountered
218   local -A statuses_seen
219
220   if __git_prompt_git rev-parse --verify refs/stash &>/dev/null; then
221     statuses_seen[STASHED]=1
222   fi
223
224   local status_lines
225   status_lines=("${(@f)${status_text}}")
226
227   # If the tracking line exists, get and parse it
228   if [[ "$status_lines[1]" =~ "^## [^ ]+ \[(.*)\]" ]]; then
229     local branch_statuses
230     branch_statuses=("${(@s/,/)match}")
231     for branch_status in $branch_statuses; do
232       if [[ ! $branch_status =~ "(behind|diverged|ahead) ([0-9]+)?" ]]; then
233         continue
234       fi
235       local last_parsed_status=$prefix_constant_map[$match[1]]
236       statuses_seen[$last_parsed_status]=$match[2]
237     done
238   fi
239
240   # For each status prefix, do a regex comparison
241   for status_prefix in ${(k)prefix_constant_map}; do
242     local status_constant="${prefix_constant_map[$status_prefix]}"
243     local status_regex=$'(^|\n)'"$status_prefix"
244
245     if [[ "$status_text" =~ $status_regex ]]; then
246       statuses_seen[$status_constant]=1
247     fi
248   done
249
250   # Display the seen statuses in the order specified
251   local status_prompt
252   for status_constant in $status_constants; do
253     if (( ${+statuses_seen[$status_constant]} )); then
254       local next_display=$constant_prompt_map[$status_constant]
255       status_prompt="$next_display$status_prompt"
256     fi
257   done
258
259   echo $status_prompt
260 }
261
262 # Outputs the name of the current user
263 # Usage example: $(git_current_user_name)
264 function git_current_user_name() {
265   __git_prompt_git config user.name 2>/dev/null
266 }
267
268 # Outputs the email of the current user
269 # Usage example: $(git_current_user_email)
270 function git_current_user_email() {
271   __git_prompt_git config user.email 2>/dev/null
272 }
273
274 # Output the name of the root directory of the git repository
275 # Usage example: $(git_repo_name)
276 function git_repo_name() {
277   local repo_path
278   if repo_path="$(__git_prompt_git rev-parse --show-toplevel 2>/dev/null)" && [[ -n "$repo_path" ]]; then
279     echo ${repo_path:t}
280   fi
281 }