Source code for utool.util_git

#!/usr/bin/env python2.7
"""
TODO: export from utool
"""
# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function
import sys
import os
from six.moves import zip
from os.path import exists, join, dirname, split, isdir
from utool._internal import meta_util_git as mu  # NOQA
from utool._internal.meta_util_git import get_repo_dirs, get_repo_dname  # NOQA
from utool import util_inject
from utool import util_arg
print, rrr, profile = util_inject.inject2(__name__, '[git]')

repo_list = mu.repo_list
set_userid = mu.set_userid

#try:
#    import __REPOS__
#    PROJECT_REPO_DIRS = __REPOS__.PROJECT_REPO_DIRS
#    PROJECT_REPO_URLS = __REPOS__.PROJECT_REPO_URLS
#except ImportError:
PROJECT_REPO_DIRS = []
PROJECT_REPO_URLS = []
CODE_DIR = None
DRY_RUN = util_arg.get_argflag('--dryrun')


[docs]def set_code_dir(code_dir): global CODE_DIR CODE_DIR = code_dir
[docs]def set_project_repos(repo_urls, repo_dirs): global PROJECT_REPO_DIRS global PROJECT_REPO_URLS PROJECT_REPO_URLS = repo_urls PROJECT_REPO_DIRS = repo_dirs
[docs]def get_project_repo_dirs(): global PROJECT_REPO_DIRS return PROJECT_REPO_DIRS
[docs]def repocmd(repo, command, sudo=False, dryrun=DRY_RUN): """ TODO change name to repo command runs a command on a repo """ import utool as ut if ut.WIN32: assert not sudo, 'cant sudo on windows' if not isinstance(command, (tuple, list)): command_list = [command] else: command_list = command cmdstr = '\n '.join([cmd_ for cmd_ in command_list]) print('+--- *** repocmd(%s) *** ' % (cmdstr,)) print('repo=%s' % ut.color_text(repo, 'yellow')) os.chdir(repo) #if command.find('git') != 0: # command = 'git ' + command ret = None if not dryrun: for count, cmd in enumerate(command_list): #assert cmd.startswith('git '), 'invalid git command' if not sudo or sys.platform.startswith('win32'): ret = os.system(cmd) else: ret = os.system('sudo ' + cmd) verbose = True if verbose > 1: print('ret(%d) = %r' % (count, ret,)) if ret != 0: raise Exception('Failed command %r' % (cmd,)) print('L____')
""" def squash_consecutive_commits_with_same_message(): http://stackoverflow.com/questions/8226278/git-alias-to-squash-all-commits-with-a-particular-commit-message # Can do interactively with this. Can it be done automatically and pay attention to # Timestamps etc? git rebase --interactive HEAD~40 --autosquash git rebase --interactive $(git merge-base HEAD master) --autosquash # Lookbehind correct version %s/\([a-z]* [a-z0-9]* wip\n\)\@<=pick \([a-z0-9]*\) wip/squash \2 wip/gc # THE FULL NON-INTERACTIVE AUTOSQUASH SCRIPT # TODO: Dont squash if there is a one hour timedelta between commits GIT_EDITOR="cat $1" GIT_SEQUENCE_EDITOR="python -m utool.util_git --exec-git_sequence_editor_squash --fpath $1" git rebase -i $(git rev-list HEAD | tail -n 1) --autosquash --no-verify GIT_EDITOR="cat $1" GIT_SEQUENCE_EDITOR="python -m utool.util_git --exec-git_sequence_editor_squash --fpath $1" git rebase -i HEAD~10 --autosquash --no-verify GIT_EDITOR="cat $1" GIT_SEQUENCE_EDITOR="python -m utool.util_git --exec-git_sequence_editor_squash --fpath $1" git rebase -i $(git merge-base HEAD master) --autosquash --no-verify # 14d778fa30a93f85c61f34d09eddb6d2cafd11e2 # c509a95d4468ebb61097bd9f4d302367424772a3 # b0ffc26011e33378ee30730c5e0ef1994bfe1a90 # GIT_SEQUENCE_EDITOR=<script> git rebase -i <params> # GIT_SEQUENCE_EDITOR="echo 'FOOBAR $1' " git rebase -i HEAD~40 --autosquash # git checkout master # git branch -D tmp # git checkout -b tmp # option to get the tail commit $(git rev-list HEAD | tail -n 1) # GIT_SEQUENCE_EDITOR="python -m utool.util_git --exec-git_sequence_editor_squash --fpath $1" git rebase -i HEAD~40 --autosquash # GIT_SEQUENCE_EDITOR="python -m utool.util_git --exec-git_sequence_editor_squash --fpath $1" git rebase -i HEAD~40 --autosquash --no-verify <params> """
[docs]def git_sequence_editor_squash(fpath): """ squashes wip messages CommandLine: python -m utool.util_git --exec-git_sequence_editor_squash Example: >>> # SCRIPT >>> import utool as ut >>> from utool.util_git import * # NOQA >>> fpath = ut.get_argval('--fpath', str, default=None) >>> git_sequence_editor_squash(fpath) Ignore: text = ut.codeblock( ''' pick 852aa05 better doctest for tips pick 3c779b8 wip pick 02bc21d wip pick 1853828 Fixed root tablename pick 9d50233 doctest updates pick 66230a5 wip pick c612e98 wip pick b298598 Fixed tablename error pick 1120a87 wip pick f6c4838 wip pick 7f92575 wip ''') """ # print(sys.argv) import utool as ut text = ut.read_from(fpath) # print('fpath = %r' % (fpath,)) print(text) # Doesnt work because of fixed witdth requirement # search = (ut.util_regex.positive_lookbehind('[a-z]* [a-z0-9]* wip\n') + 'pick ' + # ut.named_field('hash', '[a-z0-9]*') + ' wip') # repl = ('squash ' + ut.bref_field('hash') + ' wip') # import re # new_text = re.sub(search, repl, text, flags=re.MULTILINE) # print(new_text) prev_msg = None prev_dt = None new_lines = [] def get_commit_date(hashid): out, err, ret = ut.cmd('git show -s --format=%ci ' + hashid, verbose=False, quiet=True, pad_stdout=False) # from datetime import datetime from dateutil import parser # print('out = %r' % (out,)) stamp = out.strip('\n') # print('stamp = %r' % (stamp,)) dt = parser.parse(stamp) # dt = datetime.strptime(stamp, '%Y-%m-%d %H:%M:%S %Z') # print('dt = %r' % (dt,)) return dt for line in text.split('\n'): commit_line = line.split(' ') if len(commit_line) < 3: prev_msg = None prev_dt = None new_lines += [line] continue action = commit_line[0] hashid = commit_line[1] msg = ' ' .join(commit_line[2:]) try: dt = get_commit_date(hashid) except ValueError: prev_msg = None prev_dt = None new_lines += [line] continue orig_msg = msg can_squash = action == 'pick' and msg == 'wip' and prev_msg == 'wip' if prev_dt is not None and prev_msg == 'wip': tdelta = dt - prev_dt # Only squash closely consecutive commits threshold_minutes = 45 td_min = (tdelta.total_seconds() / 60.) # print(tdelta) can_squash &= td_min < threshold_minutes msg = msg + ' -- tdelta=%r' % (ut.get_timedelta_str(tdelta),) if can_squash: new_line = ' ' .join(['squash', hashid, msg]) new_lines += [new_line] else: new_lines += [line] prev_msg = orig_msg prev_dt = dt new_text = '\n'.join(new_lines) def get_commit_date(hashid): out = ut.cmd('git show -s --format=%ci ' + hashid, verbose=False) print('out = %r' % (out,)) # print('Dry run') print(new_text) ut.write_to(fpath, new_text, n=None) # ut.dump_autogen_code(fpath, new_text)
[docs]def rename_branch(old_branch_name, new_branch_name, repo='.', remote='origin', dryrun=DRY_RUN): r""" References: http://stackoverflow.com/questions/1526794/rename-master-branch-for-both-local-and-remote-git-repositories?answertab=votes#tab-top http://stackoverflow.com/questions/9524933/renaming-a-branch-in-github CommandLine: python -m utool.util_git --test-rename_branch --old=mymaster --new=ibeis_master --dryrun Example: >>> # SCRIPT >>> from utool.util_git import * # NOQA >>> repo = ut.get_argval('--repo', str, '.') >>> remote = ut.get_argval('--remote', str, 'origin') >>> old_branch_name = ut.get_argval('--old', str, None) >>> new_branch_name = ut.get_argval('--new', str, None) >>> rename_branch(old_branch_name, new_branch_name, repo, remote) """ import utool as ut repo = ut.truepath(repo) fmtdict = dict(remote=remote, old_branch_name=old_branch_name, new_branch_name=new_branch_name) command_list = [ 'git checkout {old_branch_name}'.format(**fmtdict), # rename branch 'git branch -m {old_branch_name} {new_branch_name}'.format(**fmtdict), # delete old branch 'git push {remote} :{old_branch_name}'.format(**fmtdict), # push new branch 'git push {remote} {new_branch_name}'.format(**fmtdict), ] repocmd(repo, command_list, dryrun=dryrun)
[docs]def std_build_command(repo='.'): """ My standard build script names. Calls mingw_build.bat on windows and unix_build.sh on unix """ import utool as ut print('+**** stdbuild *******') print('repo = %r' % (repo,)) if sys.platform.startswith('win32'): # vtool --rebuild-sver didnt work with this line #scriptname = './mingw_build.bat' scriptname = 'mingw_build.bat' else: scriptname = './unix_build.sh' if repo == '': # default to cwd repo = '.' else: os.chdir(repo) ut.assert_exists(scriptname) normbuild_flag = '--no-rmbuild' if ut.get_argflag(normbuild_flag): scriptname += ' ' + normbuild_flag # Execute build ut.cmd(scriptname) #os.system(scriptname) print('L**** stdbuild *******')
[docs]def gg_command(command, sudo=False, repo_dirs=None): """ Runs a command on all of your PROJECT_REPO_DIRS """ if repo_dirs is None: repo_dirs = PROJECT_REPO_DIRS print('+------- GG_COMMAND -------') print('| sudo=%s' % sudo) print('| command=%s' % command) for repo in repo_dirs: if exists(repo): repocmd(repo, command, sudo=sudo) print('L___ FINISHED GG_COMMAND ___')
[docs]def checkout_repos(repo_urls, repo_dirs=None, checkout_dir=None): """ Checkout every repo in repo_urls into checkout_dir """ # Check out any repo you dont have if checkout_dir is not None: repo_dirs = mu.get_repo_dirs(repo_urls, checkout_dir) assert repo_dirs is not None, 'specify checkout dir or repo_dirs' for repodir, repourl in zip(repo_dirs, repo_urls): print('[git] checkexist: ' + repodir) if not exists(repodir): mu.cd(dirname(repodir)) mu.cmd('git clone ' + repourl)
[docs]def ensure_project_repos(): ensure_repos(PROJECT_REPO_URLS, PROJECT_REPO_DIRS)
[docs]def ensure_repos(repo_urls, repo_dirs=None, checkout_dir=None): """ Checkout every repo in repo_urls into checkout_dir """ # Check out any repo you dont have if checkout_dir is not None: repo_dirs = mu.get_repo_dirs(repo_urls, checkout_dir) assert repo_dirs is not None, 'specify checkout dir or repo_dirs' for repodir, repourl in zip(repo_dirs, repo_urls): print('[git] checkexist: ' + repodir) if not exists(repodir): mu.cd(dirname(repodir)) mu.cmd('git clone ' + repourl) return repo_dirs
[docs]def setup_develop_repos(repo_dirs): """ Run python installs """ for repodir in repo_dirs: print('\n[git] Setup Develop: ' + repodir) mu.cd(repodir) assert exists('setup.py'), 'cannot setup a nonpython repo' mu.cmd('python setup.py develop')
[docs]def pull_repos(repo_dirs, repos_with_submodules=[]): for repodir in repo_dirs: print('Pulling: ' + repodir) mu.cd(repodir) assert exists('.git'), 'cannot pull a nongit repo' mu.cmd('git pull') reponame = split(repodir)[1] if reponame in repos_with_submodules or\ repodir in repos_with_submodules: repos_with_submodules mu.cmd('git submodule init') mu.cmd('git submodule update')
[docs]def is_gitrepo(repo_dir): gitdir = join(repo_dir, '.git') return exists(gitdir) and isdir(gitdir)
""" Rename branch from flann temp export OLD_BRANCH_NAME=new_addpoints export NEW_BRANCH_NAME=new_python_bindings #git branch -m $OLD_BRANCH_NAME $NEW_BRANCH_NAME # References: # http://stackoverflow.com/questions/1526794/rename-master-branch-for-both-local-and-remote-git-repositories?answertab=votes#tab-top # http://stackoverflow.com/questions/9524933/renaming-a-branch-in-github git branch -m $OLD_BRANCH_NAME $NEW_BRANCH_NAME git push origin :$OLD_BRANCH_NAME # delete old branch git push origin $NEW_BRANCH_NAME # push new branch """ if __name__ == '__main__': import multiprocessing import utool as ut # NOQA multiprocessing.freeze_support() # for win32 if ut.doctest_was_requested(): ut.doctest_funcs() else: command = ' '.join(sys.argv[1:]) # Apply command to all repos gg_command(command)