├── LICENSE ├── README.md ├── fastai-make-pr-branch ├── fastai-make-pr-branch-py └── hub-install.py /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # git-tools 2 | 3 | Helper scripts and tools that improve productivity for git users. 4 | 5 | Docs associated with these tools can be found [here](https://docs.fast.ai/dev/git.html). 6 | 7 | ## Index 8 | 9 | - `hub-install.py`: A python script that automates the installation of hub tool based on your system platform. `hub` is a beginner-friendly tool that wraps the common git operations with simple commands. To learn more about using `hub`, see the [docs](https://hub.github.com/hub.1.html). 10 | 11 | - `fastai-make-pr-branch`: This bash script will checkout a forked version of the original fastai repository, sync it with the original, create a new branch and set it up for a PR. **Windows users** need to use the `fastai-make-pr-branch-py` script, which currently requires the user to have a github token configured. 12 | -------------------------------------------------------------------------------- /fastai-make-pr-branch: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # for usage and functionality information run this program w/o arguments 4 | 5 | me=$(basename "$0") 6 | if [[ "$#" -ne 4 ]]; then 7 | echo "This program will checkout a forked version of the original repository, sync it with the original, create a new branch and set it up for a PR." 8 | echo "" 9 | echo "usage:" 10 | echo "" 11 | echo "$me auth user_name repo_name new_branch_name" 12 | echo "" 13 | echo "parameters:" 14 | echo "" 15 | echo " auth: ssh or https (use ssh if your github account is setup to use ssh)" 16 | echo " user: your github username" 17 | echo " repo: repository name to fork/use" 18 | echo " branch: name of the branch to create" 19 | echo "" 20 | echo "example:" 21 | echo "" 22 | echo "$me ssh my-github-username fastai new-feature-branch" 23 | echo "" 24 | echo "notes:" 25 | echo "" 26 | echo "- if the original repository has been already forked, it'll be done by the program (it will ask you for your github password)" 27 | echo "- if the program is run from a directory that already contains a checked out git repository that matches the parameters, it will re-use it instead of making a new checkout." 28 | echo "- if the requested branch already exists it'll reuse it" 29 | echo "- if the master is out of sync with the original master it'll sync it first" 30 | echo "" 31 | exit 0 32 | fi 33 | 34 | auth="$1" 35 | user_name="$2" 36 | repo_name="$3" 37 | new_branch_name="$4" 38 | 39 | orig_user_name="fastai" 40 | path="$repo_name-$new_branch_name" 41 | 42 | # abort the script on any failure 43 | set -e 44 | 45 | # check curl is available 46 | function check_curl_is_available { 47 | command -v curl >/dev/null 2>&1 || { echo >&2 "$me requires 'curl' but it's not installed. Aborting."; exit 1; } 48 | } 49 | 50 | function run() { 51 | local cmd="$@" 52 | echo "running: $cmd" 53 | eval $cmd || exit 1 54 | } 55 | 56 | function master_sync() { 57 | run "git fetch upstream" 58 | run "git checkout master" 59 | # XXX: this will fail if the user was committing directly to their forked `master` branch is 60 | cmd="git merge --no-edit upstream/master" 61 | echo "running: $cmd" 62 | eval $cmd || { git merge --abort; echo -e >&2 "\nError: $user_name/$repo_name/master can't be merged with orig_user_name/$repo_name/master.\nYou must merge manually, or reset $user_name/$repo_name/master.\nSee: https://docs-dev.fast.ai/git.html#how-to-reset-your-forked-master-branch\nAborting."; exit 1; } 63 | run "git push" 64 | 65 | } 66 | 67 | if [[ "$auth" == "ssh" ]]; then 68 | prefix="git@github.com:" 69 | else 70 | prefix="https://github.com/" 71 | fi 72 | url="$prefix$user_name/$repo_name.git" 73 | 74 | # fork the repo if it hasn't been forked yet 75 | echo -e "\n\n*** Checking if we need to fork" 76 | was_already_forked=0 77 | git ls-remote $url >/dev/null 2>&1 && was_already_forked=1 78 | if [[ "$was_already_forked" -eq 1 ]]; then 79 | echo "$url is already forked" 80 | else 81 | echo "$url doesn't exist - need to fork" 82 | check_curl_is_available 83 | run "curl -u $user_name https://api.github.com/repos/fastai/$repo_name/forks -d ''" 84 | fi 85 | 86 | # support an already existing checkout if it matches the desired parameters, but only if it's not dirty 87 | clone_is_needed=1 88 | if [[ -e ".git" ]]; then 89 | echo "detected current dir to be a git repository" 90 | if [[ -n "$(git status -s)" ]]; then 91 | echo "git status is not clean, not going to re-use the current checkout" 92 | clone_is_needed=1 93 | else 94 | cur_url=$(git config --get remote.origin.url) 95 | if [[ "$cur_url" == "$url" ]]; then 96 | echo "already on the right repo, going to re-use the current checkout" 97 | clone_is_needed=0 98 | fi 99 | fi 100 | fi 101 | 102 | # clone 103 | if [[ "$clone_is_needed" -eq 1 ]]; then 104 | echo -e "\n\n*** Cloning $url to $path" 105 | run "git clone $url $path" 106 | run "cd $path" 107 | fi 108 | 109 | # not all repos have this tool 110 | tool="tools/run-after-git-clone" 111 | if [[ -f $tool ]]; then 112 | echo -e "\n\n*** run fastai repository setup" 113 | run "python $tool" 114 | fi 115 | 116 | # setup this fork to track the upstream 117 | echo -e "\n\n*** Setting up this fork to track the upstream" 118 | orig_repo_url="$prefix$orig_user_name/$repo_name.git" 119 | run "git remote add upstream $orig_repo_url || echo 'already exists'" 120 | # show the setup 121 | run "git remote -v" 122 | 123 | # let's sync the forked repo master with the upstream master if it hasn't been just forked 124 | if [[ "$was_already_forked" -eq 1 ]]; then 125 | echo -e "\n\n*** Syncing $user_name/master with $orig_user_name/master" 126 | master_sync 127 | echo "Done" 128 | fi 129 | 130 | # now create a branch 131 | echo -e "\n\n*** Creating '$new_branch_name' branch and setting its upstream for an easy push" 132 | branch_already_exists=0 133 | git show-branch $new_branch_name >/dev/null 2>&1 && branch_already_exists=1 134 | if [[ "$branch_already_exists" -eq 1 ]]; then 135 | echo "$new_branch_name already exists, switching to it" 136 | run "git checkout $new_branch_name" 137 | else 138 | echo "$new_branch_name doesn't yet exist, creating" 139 | run "git checkout -b $new_branch_name" 140 | echo -e "'$new_branch_name' branch was successfully created" 141 | fi 142 | 143 | # set this branch's upstream, so to enable `git push` and other git 144 | # commands without needing to pass any more arguments. 145 | echo -e "\n\n*** Set this $new_branch_name's upstream to simplify push and alikes" 146 | run "git push --set-upstream origin $new_branch_name" 147 | 148 | # give parting instructions 149 | echo -e "\n\n*** Next do: " 150 | # only suggest to chdir if we made a clone 151 | echo -e "1. Switch to the working dir:\n" 152 | if [[ "$clone_is_needed" -eq 1 ]]; then 153 | echo -e "cd $path\n" 154 | else 155 | echo -e "cd .\n" 156 | fi 157 | echo -e "2. Validate you're on the right repo/branch:\n" 158 | echo -e "echo on repo: \$(git config --get remote.origin.url | sed 's|^.*//||; s/.*@//; s/[^:/]\+[:/]//; s/.git$//') branch: \$(git branch | sed -n '/\* /s///p')\n\n" 159 | echo -e "3. And do your work.\n" 160 | echo -e "4. When you're done:\n" 161 | echo -e "git add # if you added new files" 162 | echo -e "git commit -a # if you want to commit all modified/added files" 163 | echo -e "git push # push files into $new_branch_name branch\n" 164 | echo -e "5. Then go to:\n" 165 | echo -e " https://github.com/$user_name/$repo_name/tree/$new_branch_name\n" 166 | echo "review your changes and when you're happy with what you see, submit your PR." 167 | echo "While it's very good to review your PR on github before submitting it, if you want to use a CLI solution, there are several solutions to choose from:" 168 | echo "* https://github.com/github/hub" 169 | echo "* https://github.com/jd/git-pull-request" 170 | echo "* https://github.com/jehiah/git-open-pull" 171 | echo "* https://www.npmjs.com/package/pullr" 172 | -------------------------------------------------------------------------------- /fastai-make-pr-branch-py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | This program will checkout a forked version of the original repository, sync it with the original, create a new branch and set it up for a PR. 4 | 5 | usage: 6 | 7 | {me} auth user_name repo_name new_branch_name 8 | 9 | parameters: 10 | 11 | auth: ssh or https (use ssh if your github account is setup to use ssh 12 | user: your github username 13 | repo: repository name to fork/use 14 | branch: name of the branch to create 15 | 16 | example: 17 | 18 | {me} ssh my-github-username fastai new-feature-branch 19 | 20 | notes: 21 | 22 | - if the original repository has been already forked, it'll be done by the program (it will ask you for your github password) 23 | - if the program is run from a directory that already contains a checked out git repository that matches the parameters, it will re-use it instead of making a new checkout 24 | - if the requested branch already exists it'll reuse it 25 | - if the master is out of sync with the original master it'll sync it first 26 | 27 | """ 28 | import argparse 29 | import os 30 | from os.path import basename, exists 31 | from pathlib import Path 32 | import re 33 | import sys 34 | from subprocess import call, PIPE, Popen 35 | from textwrap import dedent 36 | 37 | 38 | PY3 = sys.version_info >= (3, 0) 39 | ME = basename(sys.argv[0]) 40 | ORIG_USER_NAME = 'fastai' 41 | ENV_VAR = 'GITHUB_TOKEN' 42 | 43 | 44 | def main(): 45 | check_token() 46 | args = parse_args() 47 | was_already_forked = fork_if_needed(args) 48 | need_clone = clone_is_needed(args.url) 49 | if need_clone: 50 | clone(args.url, args.path) 51 | run_after_git_clone() 52 | track_upstream(strip_token(args.orig_repo_url)) 53 | if was_already_forked: 54 | sync_with_master(args) 55 | create_branch(args.new_branch_name) 56 | set_upstream(args.new_branch_name) 57 | print_instructions(args, need_clone) 58 | 59 | 60 | def parse_args(): 61 | parser = SysExitArgumentParser(usage=__doc__.format(me=ME)) 62 | parser.add_argument( 63 | 'auth', 64 | metavar='AUTH', choices={'https'}, # no ssh support for now 65 | help='ssh or https (use ssh if your github account is setup to use ssh)' 66 | ) 67 | parser.add_argument( 68 | 'user_name', 69 | metavar='USER', 70 | help='your github username' 71 | ) 72 | parser.add_argument( 73 | 'repo_name', 74 | metavar='REPO', 75 | help='repository name to fork/use' 76 | ) 77 | parser.add_argument( 78 | 'new_branch_name', 79 | metavar='BR', 80 | help='name of the branch to create' 81 | ) 82 | parser.add_argument( 83 | '-orig', '--orig-user-name', 84 | metavar='ORIG', required=False, default=ORIG_USER_NAME, 85 | help='name of original repository user (default: %(default)s)' 86 | ) 87 | 88 | args = parser.parse_args() 89 | args.token = os.environ[ENV_VAR] 90 | args.prefix = f'https://{args.token}@github.com/' 91 | 92 | # support token access only 93 | # if args.auth == 'ssh': 94 | # args.prefix = f'git@github.com:' 95 | # else: 96 | # args.prefix = f'https://{args.token}@github.com/' 97 | 98 | args.path = f'{args.repo_name}-{args.new_branch_name}' 99 | args.url = f'{args.prefix}{args.user_name}/{args.repo_name}.git' 100 | args.orig_repo_url = f'{args.prefix}{args.orig_user_name}/{args.repo_name}.git' 101 | 102 | return args 103 | 104 | 105 | def check_curl_is_available(): 106 | "Check if curl is available and terminate the script if it isn't." 107 | try: 108 | call(['curl'], stdout=PIPE, stderr=PIPE) 109 | except FileNotFoundError: 110 | print(f"{ME} requires 'curl' but it's not installed. Aborting.") 111 | sys.exit(1) 112 | 113 | 114 | def check_token(): 115 | if ENV_VAR not in os.environ: 116 | print(dedent(''' 117 | *** WARNING! *** 118 | 119 | The HTTPS authentication method requires to explicitly enter your Github user's credentials. 120 | Unfortunately, up to the moment the tool doesn't support secured credentials entering. Therefore, 121 | you need to have a GITHUB_TOKEN environment variable set to use HTTPS authentication method. 122 | Please use the following instruction to setup token, or switch to SSH. 123 | 124 | 1. Create an access token: 125 | 126 | https://help.github.com/en/articles/creating-a-personal-access-token-for-the-command-line 127 | 128 | 2. Copy the token and save it as an environment variable: 129 | 130 | $ echo "export GITHUB_TOKEN=" >> ~/.bashrc 131 | $ source ~/.bashrc 132 | 133 | 3. Now the token is used for an access. 134 | 135 | Also, if you would like to help to improve this tool and enable the possibility to safely 136 | enter the credentials, please submit a PR! Check the following discussion to know more: 137 | 138 | https://forums.fast.ai/t/port-fastai-make-pr-branch-from-bash-to-python/29853/28 139 | 140 | *** WARNING! *** 141 | ''')) 142 | sys.exit(1) 143 | 144 | 145 | def fork_if_needed(args): 146 | "Fork the repo if it hasn't been forked yet." 147 | url, user, orig, repo = args.url, args.user_name, args.orig_user_name, args.repo_name 148 | print('\n\n*** Checking if we need to fork') 149 | if run_and_check_success(f'git ls-remote {url} >/dev/null 2>&1'): 150 | print(f'{url} is already forked') 151 | return True 152 | else: 153 | print(f"{url} doesn't exist - need to fork") 154 | check_curl_is_available() 155 | creds = user if args.auth == 'ssh' else f'{user}:{args.token}' 156 | run(f"curl -u {creds} https://api.github.com/repos/{orig}/{repo}/forks -d ''") 157 | return False 158 | 159 | 160 | def clone_is_needed(url): 161 | "Support an already existing checkout if it matches the desired parameters, but only if it's not dirty." 162 | if not exists('.git'): 163 | return True 164 | print('detected current dir to be a git repository') 165 | output = run_and_read_all('git status -s') 166 | if output: 167 | print('git status is not clean, not going to re-use the current checkout') 168 | return True 169 | cur_url = run_and_read_all('git config --get remote.origin.url') 170 | if cur_url != url: 171 | return True 172 | print('already on the right repo, going to re-use the current checkout') 173 | return False 174 | 175 | 176 | def clone(url, path): 177 | "Clone `url` into `path`." 178 | print(f'\n\n*** Cloning {url} to {path}') 179 | run(f'git clone {url} {path}') 180 | os.chdir(path) 181 | print(f'cd {path}') 182 | 183 | 184 | def run_after_git_clone(): 185 | "Run fastai's tools/run-after-git-clone if exists." 186 | tool = Path("tools")/"run-after-git-clone" 187 | if tool.exists(): 188 | print(f'\n\n*** run {tool}') 189 | run(f'{sys.executable} {tool}') 190 | 191 | 192 | def track_upstream(orig_repo_url): 193 | "Setup this fork to track the upstream." 194 | print('\n\n*** Setting up this fork to track the upstream') 195 | run(f"git remote add upstream {orig_repo_url} || echo 'already exists'") 196 | run('git remote -v') 197 | 198 | 199 | def sync_with_master(args): 200 | "Sync the forked repo master with the upstream master if it hasn't been just forked." 201 | user, repo = args.user_name, args.repo_name 202 | print(f'\n\n*** Syncing {user}/master with {args.orig_user_name}/master') 203 | run('git fetch upstream') 204 | run('git checkout master') 205 | # XXX: this will fail if the user was committing directly to their forked `master` branch is 206 | ok = run_and_check_success('git merge --no-edit upstream/master') 207 | if not ok: 208 | run('git merge --abort') 209 | print(dedent(f''' 210 | Error: {user}/{repo}/master can't be merged with {args.orig_user_name}/{repo}/master. 211 | You must merge manually, or reset {user}/{repo}/master. 212 | See: https://docs-dev.fast.ai/git.html#how-to-reset-your-forked-master-branch 213 | Aborting." 214 | '''), file=sys.stderr) 215 | sys.exit(1) 216 | run('git push') 217 | print('Done') 218 | 219 | 220 | def create_branch(new_branch_name): 221 | print(f"\n\n*** Creating '{new_branch_name}' branch and setting its upstream for an easy push") 222 | if run_and_check_success(f'git show-branch {new_branch_name} >/dev/null 2>&1'): 223 | print(f'{new_branch_name} already exists, switching to it') 224 | run(f'git checkout {new_branch_name}') 225 | else: 226 | print(f"{new_branch_name} doesn't yet exist, creating") 227 | run(f'git checkout -b {new_branch_name}') 228 | print(f'{new_branch_name} branch was successfully created') 229 | 230 | 231 | def set_upstream(branch): 232 | print(f"\n\n*** Set this {branch}'s upstream to simplify push and alikes") 233 | run(f'git push --set-upstream origin {branch}') 234 | 235 | 236 | def print_instructions(args, need_clone): 237 | switch_command = f'cd {args.path}' if need_clone else 'cd .' 238 | print(dedent(f''' 239 | 240 | Next do: 241 | 242 | 1. Switch to the branch dir, unless you're already there: 243 | 244 | {switch_command} 245 | 246 | 2. Validate you're on the right repo/branch by pasting the following into your bash: 247 | 248 | echo on repo: $(git config --get remote.origin.url | sed 's|^.*//||; s/.*@//; s/[^:/]\+[:/]//; s/.git$//') branch: $(git branch | sed -n '/\* /s///p') 249 | 250 | 3. And do your work. 251 | 252 | 4. When you're done: 253 | 254 | git add # if you added new files 255 | git commit -a # if you want to commit all modified/added files 256 | git push # push files into {args.new_branch_name} 257 | 258 | 5. Then go to: 259 | https://github.com/{args.user_name}/{args.repo_name}/tree/{args.new_branch_name} 260 | 261 | review your changes and when you're happy with what you see, submit your PR. 262 | 263 | While it's very good to review your PR on github before submitting it, if you want to use a CLI solution, 264 | there are several solutions to choose from: 265 | * https://github.com/github/hub" 266 | * https://github.com/jd/git-pull-request" 267 | * https://github.com/jehiah/git-open-pull" 268 | * https://www.npmjs.com/package/pullr" 269 | ''')) 270 | 271 | 272 | def strip_token(url): 273 | "Removes HTTPS token part from the URL string." 274 | return re.sub("[\d\w]+@github.com", "github.com", url) 275 | 276 | 277 | # -------------- 278 | # Shell wrappers 279 | # -------------- 280 | 281 | # some ideas are taken from: 282 | # 283 | # https://github.com/pytorch/pytorch/blob/master/torch/utils/collect_env.py 284 | # 285 | 286 | class SysExitArgumentParser(argparse.ArgumentParser): 287 | 288 | def error(self, message): 289 | print(self.usage) 290 | sys.exit(1) 291 | 292 | 293 | def run(command): 294 | print(f'running: {command}') 295 | rc, out, err = run_and_return_output(command) 296 | if rc != 0: 297 | print(err) 298 | sys.exit(1) 299 | if out.strip(): 300 | print(out) 301 | 302 | 303 | # (devforfu): Would it be better to replace with a different implementation? 304 | # 305 | # https://forums.fast.ai/t/port-fastai-make-pr-branch-from-bash-to-python/29853/20 306 | # 307 | def run_and_return_output(command): 308 | "Returns (return-code, stdout, stderr)." 309 | p = Popen(command, stdout=PIPE, stderr=PIPE, shell=True) 310 | out, err = p.communicate() 311 | rc = p.returncode 312 | if PY3 and out: out = out.decode("utf-8") 313 | if PY3 and err: err = err.decode("utf-8") 314 | return rc, out.strip(), err.strip() 315 | 316 | 317 | 318 | def run_and_read_all(command): 319 | "Runs command using run_lambda; reads and returns entire output if rc is 0." 320 | rc, out, _ = run_and_return_output(command) 321 | if rc is not 0: 322 | return None 323 | return out 324 | 325 | 326 | def run_and_check_success(command): 327 | print(f'running: {command}') 328 | rc, _, _ = run_and_return_output(command) 329 | return rc == 0 330 | 331 | 332 | if __name__ == '__main__': 333 | main() 334 | -------------------------------------------------------------------------------- /hub-install.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | This script will install Hub tool based on the user's Operating system and H/W Type. 4 | usage: 5 | python hub-install.py 6 | """ 7 | 8 | import os,requests,platform,json,subprocess,shutil 9 | import tarfile 10 | from zipfile import ZipFile 11 | 12 | debug=False 13 | os_type=platform.system().lower() 14 | machine_type=platform.machine().lower() 15 | 16 | if debug:print(f'Your OS and Machine Type is {os_type} and {machine_type}') 17 | 18 | if machine_type in ['x64', 'x86_64', 'amd64']: machine_type='amd64' 19 | if machine_type in ['386', '686']: machine_type='386' 20 | 21 | 22 | LIB_URL='https://api.github.com/repos/github/hub/releases/latest' 23 | def get_urls(LIB_URL,cache=True): 24 | # Caching done to avoid rate limiting : Atleast during the script development phase 25 | if os.path.isfile('cache.json'): 26 | temp=json.loads(open('cache.json','r').read()) 27 | else: 28 | response = requests.get(LIB_URL) 29 | temp=json.loads(response.content) 30 | cache=True 31 | links=[temp['assets'][i]['browser_download_url'] for i in range(len(temp['assets']))] 32 | version=temp['tag_name'] 33 | if cache: 34 | with open('cache.json', 'w') as outfile: 35 | json.dump(temp, outfile) 36 | return links,version 37 | 38 | def download_data(url, dest_dir='.'): 39 | if not os.path.isdir(dest_dir): 40 | os.makedirs(dest_dir) 41 | 42 | base = os.path.basename(url) 43 | temp_dest_path = os.path.join(dest_dir, base) 44 | 45 | # Download only if file doesnt exist 46 | if not os.path.isfile(temp_dest_path): 47 | with open(temp_dest_path, "wb") as f: 48 | r = requests.get(url) 49 | f.write(r.content) 50 | if debug: print('Completed Downloading') #debug 51 | 52 | if base.endswith('.tgz'): 53 | obj = tarfile.open(temp_dest_path) 54 | dest=temp_dest_path.rstrip('.tgz') 55 | 56 | elif base.endswith('.zip'): 57 | obj = ZipFile(temp_dest_path, 'r') 58 | dest=temp_dest_path.rstrip('.zip') 59 | else: 60 | raise ValueError("Unknown File Type: {0}.".format(base)) 61 | 62 | if debug: print("Extracting the Archive") 63 | 64 | obj.extractall(dest_dir) 65 | obj.close() 66 | os.remove(temp_dest_path) 67 | return dest 68 | 69 | 70 | # Identify the Corresponding URL 71 | links,version=get_urls(LIB_URL) 72 | if debug: print(f"Latest Version of Hub {version}") 73 | 74 | download_link=[link for link in links if ((os_type in link ) & (machine_type in link)) ] 75 | if len(download_link)==0: 76 | print('Platform not Supported.') 77 | sys.exit() 78 | 79 | if debug : print(f'Downloading the Archive from :{download_link[0]}') 80 | loc=download_data(download_link[0]) 81 | 82 | env = {} 83 | #Call OS Specific Installation Script: 84 | if os_type =='linux' or os_type =='mac': 85 | cmd=f'{loc}/install' 86 | # Check for Conda Based Environment 87 | if 'CONDA_DEFAULT_ENV' in os.environ.keys(): 88 | if debug : print('Script Executed inside Conda Environment') 89 | env["prefix"] = os.environ['CONDA_PREFIX'] 90 | elif os_type=='windows': 91 | cmd=f'install.bat' 92 | 93 | # Installation of hub library 94 | 95 | print(f"running: {cmd} (env={env})") 96 | my_env = {**os.environ, **env} 97 | subprocess.run(cmd.split(),shell=False, check=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=my_env) 98 | # Clean up Downloaded Files 99 | os.remove('cache.json') 100 | shutil.rmtree(loc) 101 | 102 | print("hub installed") 103 | --------------------------------------------------------------------------------