├── README.rst ├── LICENSE └── git-wizard /README.rst: -------------------------------------------------------------------------------- 1 | git-wizard - instant git magic and tricks 2 | ===== 3 | 4 | An interactive git shell utility for working efficiently. 5 | 6 | Vision: collect git tricks, troubleshooting techniques and git wisdom 7 | under one hat and perform them interactively. 8 | 9 | Beginners can enjoy learning git functionality interactively. 10 | Experienced command line git users can save time by using the wizard 11 | to perform frequent operations. 12 | 13 | For example, when you have a merge conflict the wizard first suggests 14 | that you run mergetool. 15 | 16 | .. contents:: 17 | :local: 18 | 19 | Features 20 | **** 21 | 22 | Fast 23 | ---- 24 | * Requires minimal key presses to perform the necessary actions. 25 | * Faster then raw git CLI or GUI for many actions. 26 | 27 | Comfortable 28 | ---- 29 | * Displays improved reports. 30 | * Automatically performs routine tasks. 31 | 32 | Smart 33 | ---- 34 | * Prioritizes issues by their importance. 35 | * Filters unsuitable tasks and proposes to perform suitable ones. 36 | 37 | For example, the wizard proposes to stage changes only when 38 | there are changed files. 39 | 40 | Details 41 | **** 42 | 43 | Internal checks 44 | ---- 45 | Each iteration, the wizard checks whether the repository contains collisions, 46 | operations in progress, conflicts, unmerged files, 47 | changes, stashes, ahead/behind commits, and untracked files. 48 | 49 | It performs "git fetch" periodically and automatically. 50 | 51 | It pronounces some valuable short messages audibly using the espeak application. 52 | 53 | Internal actions 54 | ---- 55 | The wizard uses following commands under the hood: init, status, add, 56 | commit, mergetool, diff, fetch, push, pull, clone, stash, log, clean, 57 | gui and gitk, espeak. 58 | 59 | Report 60 | ---- 61 | 62 | Reports the current status of the repository: 63 | 64 | git-wizard report:: 65 | 66 | root: /home/costa/Dropbox/linux/git-wizard 67 | conflicted files: 0 68 | unmerged files: 0 69 | in progress: 70 | modified files: 2 71 | head: master 72 | local branches: 2 73 | remote branches: 3 74 | stashes: 2 75 | commited: 2 minutes ago 76 | remote: origin 77 | fetch age (min): 6 78 | local commits: 3 79 | remote commits: 0 80 | action itemes: 1 81 | gone branches: 0 82 | untracked files: 3 83 | 84 | Other features 85 | ---- 86 | * Cleans up 'gone' branches and helps to keep your workspace tidy. 87 | 88 | To do 89 | **** 90 | 91 | * Analyze the details of 'in progress' status. 92 | * **You are welcome to request new features and add git tricks** 93 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /git-wizard: -------------------------------------------------------------------------------- 1 | #!/bin/zsh 2 | 3 | 4 | # Git Wizard 5 | # Interactive front end git wrapper script 6 | 7 | declare -A prop flag msg message_prev 8 | declare -a keys actprop actkey conf act 9 | 10 | echo | espeak 2> /dev/null 11 | quiet=$? 12 | 13 | action= 14 | while [[ "$1" =~ "^--" ]]; do 15 | case $1 in 16 | '--quiet') quiet=1; shift;; 17 | '--verbose') verbose=1; shift;; 18 | '--action') action=$2; shift 2;; 19 | *) 20 | echo error: unknown argument $1 21 | break 22 | ;; 23 | esac 24 | done 25 | 26 | ask() 27 | { 28 | test "$action" && REPLY=$action && return 29 | read -r -k "?$1" 30 | } 31 | 32 | reset-actions() 33 | { 34 | actprop=() 35 | actkey=() 36 | conf=() 37 | act=() 38 | restart=1 39 | } 40 | 41 | reset() 42 | { 43 | prop=() 44 | flag=() 45 | msg=() 46 | keys=() 47 | reset-actions 48 | restart=0 49 | } 50 | 51 | # function for acquiring common property 52 | prop() 53 | { 54 | k=$2 55 | flag[$k]="$1" 56 | keys+=($k) 57 | msg[$k]="${3/\%1/$k}" 58 | prop[$k]="$4" 59 | shift 4 60 | [ "$1" ] && echo unclaimed "$@" 61 | } 62 | 63 | # functions for reading git property 64 | prop_git() 65 | { 66 | test "$verbose" && echo checking $2: git $4 67 | prop "$1" "$2" "$3" "$(zsh -c "git $4" 2>/dev/null)" 68 | } 69 | 70 | # rep - property for report only 71 | rep() { prop_git "l" "$1" "$2" "$3" } 72 | 73 | # prs - property for summary and report 74 | prs() { prop_git "s" "$1" "$2" "$3" } 75 | 76 | # function for 77 | # linking available git actions 78 | # with properties 79 | 80 | # internal action 81 | acti() 82 | { 83 | test "$verbose" && echo acti "$@" 84 | actprop+=($1) 85 | #[[ "${actkey[(ie)$2]}" -le "${#actkey}" ]] && echo "duplicated key $2" 86 | actkey+=("$2") 87 | conf+=("$3") 88 | flag[$1]+=a 89 | act+=($4) 90 | shift 4 91 | [ "$1" ] && echo unclaimed "$@" 92 | } 93 | 94 | # git action 95 | actg() 96 | { 97 | acti $1 $2 $3 "git $4" 98 | } 99 | 100 | out() 101 | { 102 | local key=$1 103 | local prefix=$2 104 | local message=$3 105 | echo "$prefix \e[1m$message\e[0m" 106 | [ $quiet != 0 ] && return 107 | test "$action" && return 108 | test "$message_prev[$key]" = "$message" && return 109 | espeak -v en+f4 "$message" 2> /dev/null & 110 | message_prev[$key]="$message" 111 | } 112 | 113 | print-actions() 114 | { 115 | for n ({1..${#actprop}}) { 116 | [[ $actprop[$n] != $key ]] && continue 117 | kk+=($actkey[$n]) 118 | actk[$actkey[$n]]=$act[$n] 119 | echo " ﹝$actkey[$n]﹞ — $conf[$n]" 120 | } 121 | echo " [Enter] - continue" 122 | echo " * - exit" 123 | } 124 | 125 | # Ask user which available actions to perform 126 | perform-actions() 127 | { 128 | declare -a kk 129 | declare -A actk 130 | for key ($keys) { 131 | let n+=1 132 | v=$prop[$key] 133 | [[ ! $flag[$key] =~ a ]] && continue 134 | [[ -z "$v" || "$v" == 0 ]] && continue 135 | 136 | out "What to do with" "$(prop-print $key)?" 137 | print-actions 138 | ask '>' 139 | [ "$REPLY" = $'\n' ] && continue 140 | echo 141 | [ -z "$actk[$REPLY]" ] && return 1 142 | test "$verbose" && echo "$actk[$REPLY]" 143 | eval $actk[$REPLY] 144 | test "$action" && exit 145 | break 146 | } 147 | test "$action" && exit 148 | [ $#actk -eq 0 ] && echo "Nothing to do" && exit 149 | echo 150 | } 151 | 152 | prop-print() 153 | { 154 | key=$1 155 | m=$msg[$key] 156 | if [[ $m =~ '@v' ]]; then 157 | m=${m/@v/$prop[$key]} 158 | else 159 | m="$prop[$key] $m" 160 | fi 161 | p="$prop[$key]" 162 | m=$m-EOL 163 | if [[ $p =~ '^[0-9]+$' ]] && [ $p -gt 1 ]; then 164 | m=${m/\(s\)/s} 165 | else 166 | m=${m/es-EOL/} 167 | m=${m/commits-EOL/commit} 168 | fi 169 | m=${m/-EOL/} 170 | m=${m/\(s\)/} 171 | m=${m/_/ } 172 | echo -n "$m" 173 | } 174 | 175 | summary() 176 | { 177 | local m="Summary: head '$prop[head]' on branch '$prop[branch]'" 178 | for key in $keys; do 179 | [[ ! $flag[$key] =~ s ]] && continue 180 | p="$prop[$key]" 181 | [ -z "$p" -o "$p" = 0 ] && continue 182 | m+=", $(prop-print $key)" 183 | done 184 | out "" "" $m 185 | } 186 | 187 | report() 188 | { 189 | summary 190 | for key in $keys; do 191 | m=${msg[$key]/\(s\)/s} 192 | m=${m/ \@v/} 193 | m=${m/ \'\@v\'/} 194 | m=${m//_/ } 195 | [ -z $msg[$key] ] && continue 196 | echo "$m: $prop[$key]" 197 | done 198 | wait 199 | #git branch --all 200 | echo 201 | } 202 | 203 | git-get-file() 204 | { 205 | REPLY= 206 | cat $prop[root]/.git/$1 2> /dev/null | read 207 | echo "$REPLY" 208 | } 209 | 210 | git-status-parse() 211 | { 212 | local mm=$(git-get-file MERGE_MSG) 213 | local mh=$(git-get-file MERGE_HEAD) 214 | if [[ "$mh" ]]; then prop[in_progress]=merge; fi 215 | local oc=$(git-get-file rebase-apply/original-commit) 216 | local fcm=$(git-get-file rebase-apply/final-commit) 217 | local sc=$(git-get-file rebase-merge/stopped-sha) # also REBASE_HEAD) # 218 | local sm=$(git-get-file rebase-merge/message) # also MERGE_MSG 219 | test "$oc" && actg in_progress s "Show current patch '$fcm'" "show $oc" 220 | test "$sc" && actg in_progress s "Show current patch '$sm'" "show $sc" 221 | git status --untracked-files=no | while read a; do \ 222 | if [[ "$a" =~ "currently (.*) commit ([^.]*)" ]]; then prop[in_progress]=$match[1]; hash=$match[2]; fi 223 | [[ "$a" =~ "currently ([^ ]*)" ]] && prop[in_progress]=$match[1] 224 | [[ "$a" =~ "while ([^ ]*)" ]] && prop[in_progress]=$match[1] 225 | [[ "$a" =~ 'git ((.*) --edit-todo)' ]] && actg in_progress v "view and edit $match[2] todo list" "$match[1]" 226 | [[ "$a" =~ 'git ((.*) --continue)' ]] && actg in_progress c "continue $match[2]" "$match[1]" 227 | [[ "$fcm" && "$a" =~ 'git ((.*) --skip)' ]] && actg in_progress S "skip current patch '$fcm'" "$match[1]" 228 | [[ "$sm" && "$a" =~ 'git ((.*) --skip)' ]] && actg in_progress S "skip current patch '$sm'" "$match[1]" 229 | [[ "$a" =~ 'git ((.*) --abort)' ]] && actg in_progress C "cancel $match[2]" "$match[1]" 230 | done 231 | } 232 | 233 | in_progress() 234 | { 235 | local conflict_pattern='^\(^<<<<<<< \)\|\(^=======$\)\|\(^>>>>>>> \)' 236 | #prs conflicted '%1 file(s)' "grep -e '$conflict_pattern' -- $(echo $(git diff --name-only --relative)) null \ 237 | # | wc -l | ( read c; echo \$(((c+2)/3)))" 238 | grep -s -r -n -e "$conflict_pattern" -- $(echo $(git diff --name-only --relative)) /dev/null > conflicts 239 | prop s conflicted 'conflict(s)' "$(cat conflicts | wc -l | ( read c; echo $(((c+2)/3))))" 240 | #acti conflicted 'e' "edit with vim quickfix" 'vim -q <(grep -n -e '$conflict_pattern' -- $(echo $(git diff --name-only --relative)))' 241 | #acti conflicted 'e' "edit with vim quickfix" 'vim -q <(grep -n -e '$conflict_pattern' -- $(echo $(git diff --name-only --relative)))' 242 | acti conflicted 'e' "edit with vim quickfix" 'vim -q conflicts' 243 | prs unmerged '%1 file(s)' 'ls-files --unmerged | cut -f2 | sort -u | wc -l' 244 | actg unmerged t "Run merge tool" mergetool 245 | [ $prop[conflicted] = 0 ] && actg unmerged a "Add" "add \$(git diff --name-only --relative)" 246 | # git diff --diff-filter=U --name-only 247 | # git add $(git diff --diff-filter=U --name-only) 248 | prop s in_progress '%1' "$(git status --untracked-files=no HEAD | grep -q -i -e "you are" -e "in progress" && echo "an operation")" 249 | actg in_progress ' ' "Check head status" 'status --untracked-files=no HEAD' # without modifered 250 | git-status-parse 251 | # TODO: 252 | # git status -uno HEAD | grep 'rebase in progress' 253 | # git commit --amend 254 | # git rebase --edit-todo 255 | 256 | } 257 | 258 | select_branch() 259 | { 260 | select a in $(git branch --format='%(refname:short)'); do break; done 261 | [ $quiet = 0 ] && espeak -v en+f4 "Selected branch $a" 2> /dev/null & 262 | echo $a 263 | } 264 | 265 | diff_to_quickfix() 266 | { 267 | local file= 268 | while read a;do 269 | [[ "$a" =~ "^\+\+\+ (.*)" ]] && file=$match 270 | [[ "$a" =~ "^@@.*\+([0-9]+)" ]] && echo "$file:$match:$a" 271 | done 272 | #perl -ne '/^\+\+\+ (.+)/ && { $f="$1"};/@@.*\+(\d+)/ &&print "$f:$1:$_\n"' 273 | } 274 | 275 | xdg-open() 276 | { 277 | 278 | test "$SSH_CLIENT" \ 279 | && ssh ${SSH_CLIENT%% *} xdg-open $PWD/modifications.html \ 280 | || /usr/bin/xdg-open "$@" 281 | } 282 | 283 | modifications() 284 | { 285 | reset-actions 286 | git diff --stat 287 | actg modified ' ' "show diff of the modifications" 'diff' 288 | actg modified 'l' "run difftool" "difftool" 289 | actg modified 'h' "html view" "diff --relative --no-prefix | pygmentize -l diff -O full -o modifications.html; xdg-open modifications.html" 290 | print-actions 291 | ask '>' 292 | [ "$REPLY" = $'\n' ] && return 293 | echo 294 | [ -z "$actk[$REPLY]" ] && return 295 | test "$verbose" && echo "$actk[$REPLY]" 296 | eval $actk[$REPLY] 297 | test "$action" && exit 298 | break 299 | } 300 | 301 | modified() 302 | { 303 | ## checks for modified and staged(cached) files and what to do with them 304 | 305 | prs modified '%1 file(s)' 'ls-files --modified | wc -l' 306 | acti modified ' ' "show modifications" "modifications" 307 | actg modified u "update stage with modifications selectively" 'add --patch' 308 | actg modified s "push into stash selectively" 'stash push --patch' 309 | actg modified d "discard selectively" 'checkout --patch' 310 | acti modified 'e' "edit with vim quickfix" "vim -q <(git diff -U0 --relative --no-prefix | diff_to_quickfix)" 311 | actg modified 'g' "gui" "gui" 312 | 313 | prs staged '%1 file(s)' 'diff --name-only --staged | wc -l' 314 | actg staged ' ' "show" 'diff --staged' 315 | acti staged e "edit with vim quickfix" "vim -q <(git diff -U0 --staged --relative --no-prefix | diff_to_quickfix)" 316 | actg staged c "commit" commit 317 | actg staged g "run GUI commit tools" citool 318 | actg staged r "unstage (reset) modifications selectively" 'reset --patch' 319 | actg staged R "unstage (reset) all modifications" reset 320 | } 321 | 322 | head-branch() 323 | { 324 | rep head "%1 '@v'" 'describe --all --contains --always' 325 | acti head ' ' 'print report' report 326 | actg head c 'show the last commit' 'show --stat' 327 | acti head e "edit with vim quickfix" "vim -q <(git show -U0 --relative --no-prefix | diff_to_quickfix)" 328 | actg head l 'list recent log' $'log --pretty="format:%ar: %ae: %h %s \e[4m%D\e[0m" --reverse -n $((LINES-2))' 329 | actg head s "check the head status" 'status --untracked-files=no HEAD' # without modifered 330 | acti head k "explore with gitk" "gitk" 331 | acti head t "text-mode interface for Git" tig 332 | actg head a "amend the last commit" "commit --amend" 333 | actg head u "undo the last commit" "reset --soft HEAD~1" 334 | 335 | rep branch "%1 '@v'" 'rev-parse --abbrev-ref HEAD' 336 | rep local_branches '%1' 'branch | wc -l' 337 | branches_format=( 338 | $'%(committerdate:short) - %(align:left,25)%(committerdate:relative)' 339 | $'%(upstream:trackshort) %(end)' 340 | $'%(align:left,25)\e[37m%(objectname:short)\e[0m \e[1m%(refname:short)\e[0m%(end)' 341 | $'%(subject)\e[3;37m. %(authorname)\e[0m') 342 | actg local_branches ' ' 'list' "for-each-ref --sort=committerdate --format '$branches_format' refs/heads" 343 | actg local_branches 's' 'switch' 'switch $(select_branch)' 344 | rep remote_branches '%1' 'branch --remote | wc -l' 345 | actg remote_branches ' ' 'list recent' "branch --remotes --sort=committerdate --format '$branches_format' | tail -n $((LINES-2))" 346 | 347 | prs stashes '%1' 'stash list | wc -l' 348 | actg stashes ' ' "show and list stash" 'stash show; git stash list --stat' 349 | actg stashes o "pop from stash" 'stash pop' 350 | actg stashes d "drop the topmost stash" 'stash drop' 351 | rep commited '%1' 'log -1 --format="%ar"' 352 | } 353 | 354 | remote() 355 | { 356 | rep remote '%1' "config --get branch.$prop[branch].remote" 357 | local h=$git_dir/FETCH_HEAD 358 | test -e $h && 359 | prop l fetch_age '%1 (min) @v' \ 360 | "$(((`date +%s` - `stat -c %Z $h`) / 60))" 361 | #((fetch= ! ${#fetch_age} || $prop[fetch_age] > 10 || $prop[fetch_age] < 0)) 362 | if [[ -z "$prop[fetch_age]" || "$prop[fetch_age]" -gt 360 || "$prop[fetch_age]" -lt 0 ]]; then 363 | git fetch --all --prune 364 | 365 | prs gone_branches '%1' "branch -vv | grep ': gone]' | wc -l" 366 | actg gone_branches 'd' "delete merged gone branches" 'branch -d $(git branch --format="%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(else)%(end)")' 367 | actg gone_branches 'D' "delete all gone branches" 'branch -D $(git branch --format="%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(else)%(end)")' 368 | fi 369 | if [[ $prop[remote] ]]; then 370 | prs local_commits '%1' 'rev-list --count @{push}..' 371 | actg local_commits ' ' "list" "log @{push}.." 372 | actg local_commits p "Push to remote" push 373 | rep remote_commits '%1' 'rev-list --count HEAD..@{u}' 374 | actg remote_commits ' ' "list" 'log --stat HEAD..@{u}' 375 | actg remote_commits l "pull from remote" 'pull --autostash' 376 | else # when there is no remote assigned 377 | rep head_remote '%1' "config --get branch.$prop[head].remote" 378 | if hr=$prop[head_remote] && [ $prop[branch] != HEAD ]; then 379 | echo head_remote $prop[head_remote] 380 | echo hr $hr 381 | echo branch $prop[branch] 382 | actg branch p "Push new branch to remote $hr" "push --set-upstream $hr $prop[branch]" 383 | fi 384 | fi 385 | } 386 | 387 | action_itemes() 388 | { 389 | rep action_itemes '%1' 'grep --no-messages --max-depth=2 -w -eTODO -eFIXME | wc -l' 390 | actg action_itemes ' ' list 'grep --no-messages -w -n -eTODO -eFIXME' 391 | acti action_itemes 'e' edit 'vim -q <(git grep -w -n -eTODO -eFIXME)' 392 | } 393 | 394 | untracked() 395 | { 396 | prs untracked '%1 file(s)' 'ls-files --others --exclude-standard --directory| wc -l' 397 | actg untracked ' ' "list" 'status --untracked-files=normal' 398 | actg untracked a "add and stage" 'add --interactive' 399 | actg untracked c "cleanup" 'clean --interactive -d' 400 | acti untracked r "remove selectively" 'rm --recursive --interactive=always $(git ls-files --others --directory --exclude-standard --exclude .gitignore)' 401 | actg untracked i "ignore" \ 402 | 'ls-files --others --directory --exclude-standard --exclude .gitignore \ 403 | >> .gitignore' 404 | } 405 | 406 | git-general() 407 | { 408 | # rep - property for report only, prs - long and short 409 | git_dir=$(git rev-parse --git-dir) 410 | rep root '%1 @v' 'rev-parse --show-toplevel' 411 | 412 | in_progress 413 | modified 414 | head-branch 415 | remote 416 | action_itemes 417 | untracked 418 | } 419 | 420 | gitw-start() 421 | { 422 | case $1 in 423 | report) 424 | git-general 425 | report 426 | exit;; 427 | '') 428 | true 429 | until [ $? -eq 1 ] ; do 430 | reset 431 | git-general 432 | # summary 433 | perform-actions 434 | done 435 | ;; 436 | *) echo Unknown command $1. ;; 437 | esac 438 | } 439 | 440 | fail() 441 | { 442 | let fails+=1 443 | set | grep HIST 444 | history 445 | setopt 446 | echo 447 | } 448 | 449 | [ "$1" = unit-tests ] && 450 | { 451 | local fails=0 452 | #d=$(mktemp -d) 453 | d=/tmp/git-wizard-test 454 | rm -rf $d 455 | mkdir $d 456 | pushd $d 457 | touch empty 458 | HISTFILE=qqq 459 | SAVEHIST=3 460 | set -o pipefail 461 | git-wizard --action y | grep Initialized || fail 462 | ls -A 463 | #git-wizard 464 | popd 465 | #rm -rf $d 466 | echo Fails: $fails 467 | exit $fails 468 | } 469 | 470 | if [ $(git rev-parse --show-toplevel 2> /dev/null) ]; then 471 | gitw-start "$@" 472 | else 473 | out "" "" "Here is no a git repository" 474 | for c in $(xsel) $(xsel --clipboard); do 475 | if _=$(expr match "$c" ".*:.*/.*git.*"); then 476 | echo "Clipboard content looks like git url: $c" 477 | ask 'Clone? (y/n)' 478 | echo 479 | [[ $REPLY =~ ^[Yy]$ ]] && git clone "$c" 480 | fi 481 | done 482 | ask 'Create empty? (y/n)' 483 | echo 484 | [[ $REPLY =~ ^[Yy]$ ]] && git init . 485 | test -d .git 486 | fi 487 | exit $? 488 | --------------------------------------------------------------------------------