Neels Hofmeyr | b459b6c | 2018-10-31 21:35:36 +0100 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
| 2 | # |
| 3 | # (C) 2018 by Neels Hofmeyr <neels@hofmeyr.de> |
| 4 | # All rights reserved. |
| 5 | # |
| 6 | # This program is free software: you can redistribute it and/or modify |
| 7 | # it under the terms of the GNU General Public License as published by |
| 8 | # the Free Software Foundation, either version 3 of the License, or |
| 9 | # (at your option) any later version. |
| 10 | # |
| 11 | # This program is distributed in the hope that it will be useful, |
| 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | # GNU General Public License for more details. |
| 15 | # |
| 16 | # You should have received a copy of the GNU General Public License |
| 17 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 18 | |
| 19 | import sys |
| 20 | import subprocess |
Neels Hofmeyr | b459b6c | 2018-10-31 21:35:36 +0100 | [diff] [blame] | 21 | import argparse |
| 22 | import os |
| 23 | import shlex |
| 24 | |
| 25 | doc = '''gits: conveniently manage several git subdirectories. |
| 26 | Instead of doing the 'cd foo; git status; cd ../bar; git status' dance, this |
| 27 | helps to save your time with: status, fetch, rebase, ... |
| 28 | ''' |
| 29 | |
Neels Hofmeyr | b459b6c | 2018-10-31 21:35:36 +0100 | [diff] [blame] | 30 | |
| 31 | def error(*msgs): |
| 32 | sys.stderr.write(''.join(msgs)) |
| 33 | sys.stderr.write('\n') |
| 34 | exit(1) |
| 35 | |
| 36 | |
| 37 | def cmd_to_str(cmd): |
| 38 | return ' '.join(shlex.quote(c) for c in cmd) |
| 39 | |
| 40 | |
| 41 | def git(git_dir, *args, may_fail=False, section_marker=False, show_cmd=True): |
| 42 | sys.stdout.flush() |
| 43 | sys.stderr.flush() |
| 44 | |
| 45 | if section_marker: |
| 46 | print('\n===== %s =====' % git_dir) |
| 47 | sys.stdout.flush() |
| 48 | |
| 49 | cmd = ['git', '-C', git_dir] + list(args) |
| 50 | if show_cmd: |
| 51 | print('+ %s' % cmd_to_str(cmd)) |
| 52 | sys.stdout.flush() |
| 53 | |
| 54 | rc = subprocess.call(cmd) |
| 55 | if rc and not may_fail: |
| 56 | error('git returned error! command: git -C %r %s' % |
| 57 | (git_dir, ' '.join(repr(arg) for arg in args))) |
| 58 | |
| 59 | |
| 60 | def git_output(git_dir, *args): |
Neels Hofmeyr | 68d8f34 | 2018-11-12 22:44:08 +0100 | [diff] [blame] | 61 | return subprocess.check_output(['git', '-C', git_dir, ] + list(args), stderr=subprocess.STDOUT).decode('utf-8') |
Neels Hofmeyr | b459b6c | 2018-10-31 21:35:36 +0100 | [diff] [blame] | 62 | |
| 63 | |
Oliver Smith | b93f504 | 2018-11-09 10:34:36 +0100 | [diff] [blame] | 64 | def git_bool(git_dir, *args): |
| 65 | try: |
| 66 | subprocess.check_output(['git', '-C', git_dir, ] + list(args)) |
| 67 | return True |
| 68 | except subprocess.CalledProcessError as e: |
| 69 | return False |
Neels Hofmeyr | b459b6c | 2018-10-31 21:35:36 +0100 | [diff] [blame] | 70 | |
| 71 | |
Neels Hofmeyr | 68d8f34 | 2018-11-12 22:44:08 +0100 | [diff] [blame] | 72 | def git_ahead_behind(git_dir, branch, branch_upstream): |
Oliver Smith | b93f504 | 2018-11-09 10:34:36 +0100 | [diff] [blame] | 73 | ''' Count revisions ahead/behind of the remote branch. |
| 74 | returns: (ahead, behind) (e.g. (0, 5)) ''' |
Neels Hofmeyr | b459b6c | 2018-10-31 21:35:36 +0100 | [diff] [blame] | 75 | |
Oliver Smith | b93f504 | 2018-11-09 10:34:36 +0100 | [diff] [blame] | 76 | # Missing remote branch |
Neels Hofmeyr | 68d8f34 | 2018-11-12 22:44:08 +0100 | [diff] [blame] | 77 | if not branch_upstream: |
Oliver Smith | b93f504 | 2018-11-09 10:34:36 +0100 | [diff] [blame] | 78 | return (0, 0) |
Neels Hofmeyr | b459b6c | 2018-10-31 21:35:36 +0100 | [diff] [blame] | 79 | |
Neels Hofmeyr | 68d8f34 | 2018-11-12 22:44:08 +0100 | [diff] [blame] | 80 | behind = git_output(git_dir, 'rev-list', '--count', '%s..%s' % (branch, branch_upstream)) |
| 81 | ahead = git_output(git_dir, 'rev-list', '--count', '%s..%s' % (branch_upstream, branch)) |
Oliver Smith | b93f504 | 2018-11-09 10:34:36 +0100 | [diff] [blame] | 82 | return (int(ahead.rstrip()), int(behind.rstrip())) |
| 83 | |
| 84 | |
| 85 | def git_branches(git_dir, obj='refs/heads'): |
| 86 | ret = git_output(git_dir, 'for-each-ref', obj, '--format', '%(refname:short)') |
| 87 | return ret.splitlines() |
| 88 | |
| 89 | |
| 90 | def git_branch_current(git_dir): |
| 91 | ret = git_output(git_dir, 'rev-parse', '--abbrev-ref', 'HEAD').rstrip() |
| 92 | if ret == 'HEAD': |
| 93 | return None |
| 94 | return ret |
| 95 | |
| 96 | |
Neels Hofmeyr | 68d8f34 | 2018-11-12 22:44:08 +0100 | [diff] [blame] | 97 | def git_branch_upstream(git_dir, branch_name='HEAD'): |
| 98 | '''Return an upstream branch name, or an None if there is none.''' |
| 99 | try: |
| 100 | return git_output(git_dir, 'rev-parse', '--abbrev-ref', '%s@{u}' % branch_name).rstrip() |
| 101 | except subprocess.CalledProcessError: |
| 102 | return None |
| 103 | |
| 104 | |
Oliver Smith | b93f504 | 2018-11-09 10:34:36 +0100 | [diff] [blame] | 105 | def git_has_modifications(git_dir): |
Oliver Smith | 2a30b52 | 2018-11-16 16:47:57 +0100 | [diff] [blame] | 106 | return not git_bool(git_dir, 'diff', '--quiet', 'HEAD') |
Oliver Smith | b93f504 | 2018-11-09 10:34:36 +0100 | [diff] [blame] | 107 | |
| 108 | |
Neels Hofmeyr | 68d8f34 | 2018-11-12 22:44:08 +0100 | [diff] [blame] | 109 | def git_can_fast_forward(git_dir, branch, branch_upstream): |
| 110 | return git_bool(git_dir, 'merge-base', '--is-ancestor', branch, branch_upstream) |
Oliver Smith | b93f504 | 2018-11-09 10:34:36 +0100 | [diff] [blame] | 111 | |
| 112 | |
| 113 | def format_branch_ahead_behind(branch, ahead, behind): |
| 114 | ''' branch: string like "master" |
| 115 | ahead, behind: integers like 5, 3 |
| 116 | returns: string like "master", "master[+5]", "master[-3]", "master[+5|-3]" ''' |
| 117 | # Just the branch |
| 118 | if not ahead and not behind: |
| 119 | return branch |
| 120 | |
| 121 | # Suffix with ahead/behind |
| 122 | ret = branch + '[' |
| 123 | if ahead: |
| 124 | ret += '+' + str(ahead) |
| 125 | if behind: |
| 126 | ret += '|' |
| 127 | if behind: |
| 128 | ret += '-' + str(behind) |
| 129 | ret += ']' |
| 130 | return ret |
Neels Hofmeyr | b459b6c | 2018-10-31 21:35:36 +0100 | [diff] [blame] | 131 | |
| 132 | |
| 133 | def git_branch_summary(git_dir): |
| 134 | '''return a list of strings: [git_dir, branch-info0, branch-info1,...] |
| 135 | infos are are arbitrary strings like "master[-1]"''' |
| 136 | |
| 137 | interesting_branch_names = ('master',) |
| 138 | |
| 139 | strs = [git_dir, ] |
Oliver Smith | b93f504 | 2018-11-09 10:34:36 +0100 | [diff] [blame] | 140 | if git_has_modifications(git_dir): |
Neels Hofmeyr | b459b6c | 2018-10-31 21:35:36 +0100 | [diff] [blame] | 141 | strs.append('MODS') |
| 142 | |
Oliver Smith | b93f504 | 2018-11-09 10:34:36 +0100 | [diff] [blame] | 143 | branch_current = git_branch_current(git_dir) |
| 144 | for branch in git_branches(git_dir): |
| 145 | is_current = (branch == branch_current) |
| 146 | if not is_current and branch not in interesting_branch_names: |
Neels Hofmeyr | b459b6c | 2018-10-31 21:35:36 +0100 | [diff] [blame] | 147 | continue |
Oliver Smith | b93f504 | 2018-11-09 10:34:36 +0100 | [diff] [blame] | 148 | |
Neels Hofmeyr | 68d8f34 | 2018-11-12 22:44:08 +0100 | [diff] [blame] | 149 | ahead, behind = git_ahead_behind(git_dir, branch, |
| 150 | git_branch_upstream(git_dir, branch)) |
| 151 | |
Oliver Smith | b93f504 | 2018-11-09 10:34:36 +0100 | [diff] [blame] | 152 | if not ahead and not behind and not is_current: |
Neels Hofmeyr | b459b6c | 2018-10-31 21:35:36 +0100 | [diff] [blame] | 153 | # skip branches that are "not interesting" |
| 154 | continue |
Neels Hofmeyr | b459b6c | 2018-10-31 21:35:36 +0100 | [diff] [blame] | 155 | |
Neels Hofmeyr | 68d8f34 | 2018-11-12 22:44:08 +0100 | [diff] [blame] | 156 | # Branch with ahead/behind upstream info ("master[+1|-5]") |
Oliver Smith | b93f504 | 2018-11-09 10:34:36 +0100 | [diff] [blame] | 157 | strs.append(format_branch_ahead_behind(branch, ahead, behind)) |
Neels Hofmeyr | b459b6c | 2018-10-31 21:35:36 +0100 | [diff] [blame] | 158 | return strs |
| 159 | |
| 160 | |
| 161 | def format_summaries(summaries, sep0=' ', sep1=' '): |
| 162 | first_col = max([len(row[0]) for row in summaries]) |
| 163 | first_col_fmt = '%' + str(first_col) + 's' |
| 164 | |
| 165 | lines = [] |
| 166 | for row in summaries: |
| 167 | lines.append('%s%s%s' % (first_col_fmt % |
| 168 | row[0], sep0, sep1.join(row[1:]))) |
| 169 | |
| 170 | return '\n'.join(lines) |
| 171 | |
| 172 | |
| 173 | def git_dirs(): |
| 174 | dirs = [] |
| 175 | for sub in os.listdir(): |
| 176 | git_path = os.path.join(sub, '.git') |
| 177 | if not os.path.isdir(git_path): |
| 178 | continue |
| 179 | dirs.append(sub) |
| 180 | |
| 181 | if not dirs: |
| 182 | error('No subdirectories found that are git clones') |
| 183 | |
| 184 | return list(sorted(dirs)) |
| 185 | |
| 186 | |
| 187 | def print_status(): |
| 188 | infos = [git_branch_summary(git_dir) for git_dir in git_dirs()] |
| 189 | print(format_summaries(infos)) |
| 190 | |
| 191 | |
| 192 | def cmd_do(argv): |
| 193 | for git_dir in git_dirs(): |
| 194 | git(git_dir, *argv, may_fail=True, section_marker=True) |
| 195 | |
| 196 | |
| 197 | def cmd_sh(cmd): |
| 198 | if not cmd: |
| 199 | error('which command do you want to run?') |
| 200 | for git_dir in git_dirs(): |
| 201 | print('\n===== %s =====' % git_dir) |
| 202 | print('+ %s' % cmd_to_str(cmd)) |
| 203 | sys.stdout.flush() |
| 204 | subprocess.call(cmd, cwd=git_dir) |
| 205 | sys.stdout.flush() |
| 206 | sys.stderr.flush() |
| 207 | |
| 208 | |
| 209 | class SkipThisRepo(Exception): |
| 210 | pass |
| 211 | |
| 212 | |
| 213 | def ask(git_dir, *question, valid_answers=('*',)): |
| 214 | while True: |
| 215 | print('\n' + '\n '.join(question)) |
| 216 | print(' ' + '\n '.join(( |
| 217 | 's skip this repo', |
| 218 | 't show in tig', |
| 219 | 'g show in gitk', |
| 220 | ))) |
| 221 | |
| 222 | answer = sys.stdin.readline().strip() |
| 223 | if answer == 's': |
| 224 | raise SkipThisRepo() |
| 225 | if answer == 't': |
| 226 | subprocess.call(('tig', '--all'), cwd=git_dir) |
| 227 | continue |
| 228 | if answer == 'g': |
| 229 | subprocess.call(('gitk', '--all'), cwd=git_dir) |
| 230 | continue |
| 231 | |
| 232 | for v in valid_answers: |
| 233 | if v == answer: |
| 234 | return answer |
| 235 | if v == '*': |
| 236 | return answer |
| 237 | if v == '+' and len(answer): |
| 238 | return answer |
| 239 | |
| 240 | |
Neels Hofmeyr | ae79f4b | 2018-11-23 04:09:10 +0100 | [diff] [blame] | 241 | def ask_reset_hard_or_push_f(git_dir, orig_branch, upstream_branch): |
Neels Hofmeyr | dff944b | 2018-11-12 23:27:49 +0100 | [diff] [blame] | 242 | do_reset = ask(git_dir, 'Diverged.', |
| 243 | '%s: git reset --hard %s?' % ( |
| 244 | orig_branch, upstream_branch), |
| 245 | '<empty> no', |
Neels Hofmeyr | 20d95d0 | 2018-11-12 23:25:57 +0100 | [diff] [blame] | 246 | 'OK yes, reset to upstream (write OK in caps!)', |
| 247 | 'P `push -f` to overwrite upstream (P in caps!)', |
| 248 | valid_answers=('', 'OK', 'P')) |
Neels Hofmeyr | dff944b | 2018-11-12 23:27:49 +0100 | [diff] [blame] | 249 | |
| 250 | if do_reset == 'OK': |
| 251 | git(git_dir, 'reset', '--hard', upstream_branch) |
Neels Hofmeyr | 20d95d0 | 2018-11-12 23:25:57 +0100 | [diff] [blame] | 252 | elif do_reset == 'P': |
| 253 | git(git_dir, 'push', '-f') |
Neels Hofmeyr | dff944b | 2018-11-12 23:27:49 +0100 | [diff] [blame] | 254 | |
| 255 | |
Neels Hofmeyr | b459b6c | 2018-10-31 21:35:36 +0100 | [diff] [blame] | 256 | def rebase(git_dir): |
Oliver Smith | b93f504 | 2018-11-09 10:34:36 +0100 | [diff] [blame] | 257 | orig_branch = git_branch_current(git_dir) |
Neels Hofmeyr | b459b6c | 2018-10-31 21:35:36 +0100 | [diff] [blame] | 258 | if orig_branch is None: |
| 259 | print('Not on a branch: %s' % git_dir) |
| 260 | raise SkipThisRepo() |
| 261 | |
Neels Hofmeyr | 68d8f34 | 2018-11-12 22:44:08 +0100 | [diff] [blame] | 262 | upstream_branch = git_branch_upstream(git_dir, orig_branch) |
| 263 | |
Neels Hofmeyr | 94e0aec | 2019-03-15 15:34:30 +0100 | [diff] [blame] | 264 | print('Checking for rebase of %r onto %r' % (orig_branch, upstream_branch)) |
Neels Hofmeyr | 68d8f34 | 2018-11-12 22:44:08 +0100 | [diff] [blame] | 265 | ahead, behind = git_ahead_behind(git_dir, orig_branch, upstream_branch) |
Oliver Smith | b93f504 | 2018-11-09 10:34:36 +0100 | [diff] [blame] | 266 | |
| 267 | if git_has_modifications(git_dir): |
Neels Hofmeyr | b459b6c | 2018-10-31 21:35:36 +0100 | [diff] [blame] | 268 | do_commit = ask(git_dir, 'Local mods.', |
| 269 | 'c commit to this branch', |
| 270 | '<name> commit to new branch', |
| 271 | '<empty> skip') |
| 272 | |
| 273 | if not do_commit: |
| 274 | raise SkipThisRepo() |
| 275 | |
| 276 | if do_commit == 'c': |
| 277 | git(git_dir, 'commit', '-am', 'wip', may_fail=True) |
| 278 | else: |
| 279 | git(git_dir, 'checkout', '-b', do_commit) |
| 280 | git(git_dir, 'commit', '-am', 'wip', may_fail=True) |
| 281 | git(git_dir, 'checkout', orig_branch) |
| 282 | |
Oliver Smith | b93f504 | 2018-11-09 10:34:36 +0100 | [diff] [blame] | 283 | if git_has_modifications(git_dir): |
Neels Hofmeyr | b459b6c | 2018-10-31 21:35:36 +0100 | [diff] [blame] | 284 | print('There still are local modifications') |
| 285 | raise SkipThisRepo() |
| 286 | |
Oliver Smith | b93f504 | 2018-11-09 10:34:36 +0100 | [diff] [blame] | 287 | # Missing upstream branch |
Neels Hofmeyr | 68d8f34 | 2018-11-12 22:44:08 +0100 | [diff] [blame] | 288 | if not upstream_branch: |
Neels Hofmeyr | b459b6c | 2018-10-31 21:35:36 +0100 | [diff] [blame] | 289 | print('there is no upstream branch for %r' % orig_branch) |
| 290 | |
Oliver Smith | b93f504 | 2018-11-09 10:34:36 +0100 | [diff] [blame] | 291 | # Diverged |
| 292 | elif ahead and behind: |
Neels Hofmeyr | ae79f4b | 2018-11-23 04:09:10 +0100 | [diff] [blame] | 293 | ask_reset_hard_or_push_f(git_dir, orig_branch, upstream_branch) |
Oliver Smith | b93f504 | 2018-11-09 10:34:36 +0100 | [diff] [blame] | 294 | |
| 295 | # Behind |
| 296 | elif behind: |
Neels Hofmeyr | 68d8f34 | 2018-11-12 22:44:08 +0100 | [diff] [blame] | 297 | if git_can_fast_forward(git_dir, orig_branch, upstream_branch): |
Neels Hofmeyr | b459b6c | 2018-10-31 21:35:36 +0100 | [diff] [blame] | 298 | print('fast-forwarding...') |
| 299 | git(git_dir, 'merge') |
| 300 | else: |
| 301 | do_merge = ask(git_dir, 'Behind. git merge?', |
| 302 | "<empty> don't merge", |
| 303 | 'ok git merge', |
| 304 | valid_answers=('', 'ok') |
| 305 | ) |
| 306 | |
| 307 | if do_merge == 'ok': |
| 308 | git(git_dir, 'merge') |
| 309 | |
Oliver Smith | b93f504 | 2018-11-09 10:34:36 +0100 | [diff] [blame] | 310 | # Ahead |
| 311 | elif ahead: |
Neels Hofmeyr | b459b6c | 2018-10-31 21:35:36 +0100 | [diff] [blame] | 312 | do_commit = ask(git_dir, 'Ahead. commit to new branch?', |
| 313 | '<empty> no', |
| 314 | '<name> create new branch', |
| 315 | ) |
| 316 | if do_commit: |
| 317 | git(git_dir, 'checkout', '-b', do_commit) |
| 318 | git(git_dir, 'commit', '-am', 'wip', may_fail=True) |
| 319 | git(git_dir, 'checkout', orig_branch) |
| 320 | |
Neels Hofmeyr | ae79f4b | 2018-11-23 04:09:10 +0100 | [diff] [blame] | 321 | ask_reset_hard_or_push_f(git_dir, orig_branch, upstream_branch) |
Neels Hofmeyr | b459b6c | 2018-10-31 21:35:36 +0100 | [diff] [blame] | 322 | |
Neels Hofmeyr | efa34ac | 2019-03-15 15:34:45 +0100 | [diff] [blame] | 323 | if git_has_modifications(git_dir): |
| 324 | raise SkipThisRepo() |
| 325 | |
| 326 | # Rebase onto origin/master? Only when this isn't already the master branch |
| 327 | if upstream_branch != 'origin/master': |
| 328 | ahead, behind = git_ahead_behind(git_dir, orig_branch, 'origin/master') |
| 329 | |
| 330 | if ahead and behind: |
| 331 | do_rebase = ask(git_dir, '%r diverged from master. git rebase -i origin/master?' % orig_branch, |
| 332 | "<empty> don't rebase", |
| 333 | 'ok rebase onto origin/master', |
| 334 | valid_answers=('', 'ok')) |
| 335 | |
| 336 | if do_rebase == 'ok': |
| 337 | git(git_dir, 'rebase', '-i', 'origin/master') |
| 338 | # On conflicts, we'll exit with error implicitly |
| 339 | |
Neels Hofmeyr | 7f46be3 | 2019-03-29 15:42:41 +0100 | [diff] [blame] | 340 | if upstream_branch is not None: |
| 341 | do_push = ask(git_dir, 'git push -f to overwrite %r?' % upstream_branch, |
| 342 | "<empty> don't overwrite upstream", |
| 343 | 'P `push -f` to overwrite upstream (P in caps!)', |
| 344 | valid_answers=('', 'P')) |
| 345 | if do_push == 'P': |
| 346 | git(git_dir, 'push', '-f') |
Neels Hofmeyr | efa34ac | 2019-03-15 15:34:45 +0100 | [diff] [blame] | 347 | |
Neels Hofmeyr | b459b6c | 2018-10-31 21:35:36 +0100 | [diff] [blame] | 348 | return orig_branch |
| 349 | |
| 350 | |
| 351 | def cmd_rebase(): |
| 352 | skipped = [] |
| 353 | for git_dir in git_dirs(): |
| 354 | try: |
| 355 | print('\n\n===== %s =====' % git_dir) |
| 356 | sys.stdout.flush() |
| 357 | |
| 358 | branch = rebase(git_dir) |
| 359 | if branch != 'master': |
| 360 | git(git_dir, 'checkout', 'master') |
| 361 | rebase(git_dir) |
| 362 | git(git_dir, 'checkout', branch) |
| 363 | |
| 364 | except SkipThisRepo: |
| 365 | print('\nSkipping %r' % git_dir) |
| 366 | skipped.append(git_dir) |
| 367 | |
| 368 | print('\n\n==========\nrebase done.\n') |
| 369 | print_status() |
| 370 | if skipped: |
| 371 | print('\nskipped: %s' % ' '.join(skipped)) |
| 372 | |
| 373 | |
| 374 | def parse_args(): |
| 375 | parser = argparse.ArgumentParser(description=doc) |
| 376 | sub = parser.add_subparsers(title='action', dest='action') |
| 377 | sub.required = True |
| 378 | |
| 379 | # status |
| 380 | sub.add_parser('status', aliases=['st', 's'], |
| 381 | help='show a branch summary and indicate modifications') |
| 382 | |
| 383 | # fetch |
| 384 | fetch = sub.add_parser('fetch', aliases=['f'], |
| 385 | help="run 'git fetch' in each clone (use before rebase)") |
| 386 | fetch.add_argument('remainder', nargs=argparse.REMAINDER, |
| 387 | help='additional arguments to be passed to git fetch') |
| 388 | |
| 389 | # rebase |
| 390 | sub.add_parser('rebase', aliases=['r', 're'], |
| 391 | help='interactively ff-merge master, rebase current branches') |
| 392 | |
| 393 | # sh |
| 394 | sh = sub.add_parser('sh', |
| 395 | help='run shell command in each clone (`gits sh echo hi`)') |
| 396 | sh.add_argument('remainder', nargs=argparse.REMAINDER, |
| 397 | help='command to run in each clone') |
| 398 | |
| 399 | # do |
| 400 | do = sub.add_parser('do', |
| 401 | help='run git command in each clone (`gits do clean -dxf`)') |
| 402 | do.add_argument('remainder', nargs=argparse.REMAINDER, |
| 403 | help='git command to run in each clone') |
| 404 | return parser.parse_args() |
| 405 | |
| 406 | |
| 407 | if __name__ == '__main__': |
| 408 | args = parse_args() |
| 409 | if args.action in ['status', 's', 'st']: |
| 410 | print_status() |
| 411 | elif args.action in ['fetch', 'f']: |
| 412 | cmd_do(['fetch'] + args.remainder) |
| 413 | elif args.action in ['rebase', 'r']: |
| 414 | cmd_rebase() |
| 415 | elif args.action == 'sh': |
| 416 | cmd_sh(args.remainder) |
| 417 | elif args.action == 'do': |
| 418 | cmd_do(args.remainder) |
| 419 | |
| 420 | # vim: shiftwidth=4 expandtab tabstop=4 |