<s> from __future__ import ( absolute_import , division , generators , nested_scopes , print_function , <EOL> unicode_literals , with_statement ) <EOL> import logging <EOL> import os <EOL> import StringIO <EOL> import subprocess <EOL> import traceback <EOL> from contextlib import contextmanager <EOL> from pants . scm . scm import Scm <EOL> from pants . util . contextutil import pushd <EOL> from pants . util . memo import memoized_method <EOL> from pants . util . strutil import ensure_binary <EOL> MAX_SYMLINKS_IN_REALPATH = <NUM_LIT> <EOL> GIT_HASH_LENGTH = <NUM_LIT:20> <EOL> SLASH = ensure_binary ( '<STR_LIT:/>' ) <EOL> NUL = ensure_binary ( '<STR_LIT>' ) <EOL> SPACE = ensure_binary ( '<STR_LIT:U+0020>' ) <EOL> NEWLINE = ensure_binary ( '<STR_LIT:\n>' ) <EOL> EMPTY_STRING = ensure_binary ( "<STR_LIT>" ) <EOL> logger = logging . getLogger ( __name__ ) <EOL> class Git ( Scm ) : <EOL> """<STR_LIT>""" <EOL> @ classmethod <EOL> def detect_worktree ( cls , binary = '<STR_LIT>' , subdir = None ) : <EOL> """<STR_LIT>""" <EOL> cmd = [ binary , '<STR_LIT>' , '<STR_LIT>' ] <EOL> try : <EOL> if subdir : <EOL> with pushd ( subdir ) : <EOL> process , out = cls . _invoke ( cmd ) <EOL> else : <EOL> process , out = cls . _invoke ( cmd ) <EOL> cls . _check_result ( cmd , process . returncode , raise_type = Scm . ScmException ) <EOL> except Scm . ScmException : <EOL> return None <EOL> return cls . _cleanse ( out ) <EOL> @ classmethod <EOL> def _invoke ( cls , cmd ) : <EOL> """<STR_LIT>""" <EOL> try : <EOL> process = subprocess . Popen ( cmd , stdout = subprocess . PIPE ) <EOL> except OSError as e : <EOL> raise cls . LocalException ( '<STR_LIT>' . format ( '<STR_LIT:U+0020>' . join ( cmd ) , e ) ) <EOL> out , _ = process . communicate ( ) <EOL> return process , out <EOL> @ classmethod <EOL> def _cleanse ( cls , output , errors = '<STR_LIT:strict>' ) : <EOL> return output . strip ( ) . decode ( '<STR_LIT:utf-8>' , errors = errors ) <EOL> @ classmethod <EOL> def _check_result ( cls , cmd , result , failure_msg = None , raise_type = Scm . ScmException ) : <EOL> if result != <NUM_LIT:0> : <EOL> raise raise_type ( failure_msg or '<STR_LIT>' . format ( '<STR_LIT:U+0020>' . join ( cmd ) , result ) ) <EOL> def __init__ ( self , binary = '<STR_LIT>' , gitdir = None , worktree = None , remote = None , branch = None ) : <EOL> """<STR_LIT>""" <EOL> super ( Scm , self ) . __init__ ( ) <EOL> self . _gitcmd = binary <EOL> self . _worktree = os . path . realpath ( worktree or os . getcwd ( ) ) <EOL> self . _gitdir = os . path . realpath ( gitdir ) if gitdir else os . path . join ( self . _worktree , '<STR_LIT>' ) <EOL> self . _remote = remote <EOL> self . _branch = branch <EOL> def current_rev_identifier ( self ) : <EOL> return '<STR_LIT>' <EOL> @ property <EOL> def worktree ( self ) : <EOL> return self . _worktree <EOL> @ property <EOL> def commit_id ( self ) : <EOL> return self . _check_output ( [ '<STR_LIT>' , '<STR_LIT>' ] , raise_type = Scm . LocalException ) <EOL> @ property <EOL> def server_url ( self ) : <EOL> git_output = self . _check_output ( [ '<STR_LIT>' , '<STR_LIT>' ] , raise_type = Scm . LocalException ) <EOL> def origin_urls ( ) : <EOL> for line in git_output . splitlines ( ) : <EOL> name , url , action = line . split ( ) <EOL> if name == '<STR_LIT>' and action == '<STR_LIT>' : <EOL> yield url <EOL> origins = list ( origin_urls ( ) ) <EOL> if len ( origins ) != <NUM_LIT:1> : <EOL> raise Scm . LocalException ( "<STR_LIT>" <EOL> "<STR_LIT>" . format ( git_output ) ) <EOL> return origins [ <NUM_LIT:0> ] <EOL> @ property <EOL> def tag_name ( self ) : <EOL> tag = self . _check_output ( [ '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' ] , raise_type = Scm . LocalException ) <EOL> return None if b'<STR_LIT>' in tag else tag <EOL> @ property <EOL> def branch_name ( self ) : <EOL> branch = self . _check_output ( [ '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' ] , <EOL> raise_type = Scm . LocalException ) <EOL> return None if branch == '<STR_LIT>' else branch <EOL> def fix_git_relative_path ( self , worktree_path , relative_to ) : <EOL> return os . path . relpath ( os . path . join ( self . _worktree , worktree_path ) , relative_to ) <EOL> def changed_files ( self , from_commit = None , include_untracked = False , relative_to = None ) : <EOL> relative_to = relative_to or self . _worktree <EOL> rel_suffix = [ '<STR_LIT>' , relative_to ] <EOL> uncommitted_changes = self . _check_output ( [ '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' ] + rel_suffix , <EOL> raise_type = Scm . LocalException ) <EOL> files = set ( uncommitted_changes . split ( ) ) <EOL> if from_commit : <EOL> committed_cmd = [ '<STR_LIT>' , '<STR_LIT>' , from_commit + '<STR_LIT>' ] + rel_suffix <EOL> committed_changes = self . _check_output ( committed_cmd , <EOL> raise_type = Scm . LocalException ) <EOL> files . update ( committed_changes . split ( ) ) <EOL> if include_untracked : <EOL> untracked_cmd = [ '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' ] + rel_suffix <EOL> untracked = self . _check_output ( untracked_cmd , <EOL> raise_type = Scm . LocalException ) <EOL> files . update ( untracked . split ( ) ) <EOL> return set ( self . fix_git_relative_path ( f , relative_to ) for f in files ) <EOL> def changes_in ( self , diffspec , relative_to = None ) : <EOL> relative_to = relative_to or self . _worktree <EOL> cmd = [ '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' , diffspec ] <EOL> files = self . _check_output ( cmd , raise_type = Scm . LocalException ) . split ( ) <EOL> return set ( self . fix_git_relative_path ( f . strip ( ) , relative_to ) for f in files ) <EOL> def changelog ( self , from_commit = None , files = None ) : <EOL> args = [ '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' ] <EOL> if from_commit : <EOL> args . append ( from_commit + '<STR_LIT>' ) <EOL> if files : <EOL> args . append ( '<STR_LIT>' ) <EOL> args . extend ( files ) <EOL> return self . _check_output ( args , raise_type = Scm . LocalException , errors = '<STR_LIT:replace>' ) <EOL> def merge_base ( self , left = '<STR_LIT>' , right = '<STR_LIT>' ) : <EOL> """<STR_LIT>""" <EOL> return self . _check_output ( [ '<STR_LIT>' , left , right ] , raise_type = Scm . LocalException ) <EOL> def refresh ( self , leave_clean = False ) : <EOL> """<STR_LIT>""" <EOL> remote , merge = self . _get_upstream ( ) <EOL> self . _check_call ( [ '<STR_LIT>' , '<STR_LIT>' , remote , merge ] , raise_type = Scm . RemoteException ) <EOL> try : <EOL> self . _check_call ( [ '<STR_LIT>' , '<STR_LIT>' ] , raise_type = Scm . LocalException ) <EOL> except Scm . LocalException as e : <EOL> if leave_clean : <EOL> logger . debug ( '<STR_LIT>' ) <EOL> try : <EOL> self . _check_call ( [ '<STR_LIT>' , '<STR_LIT>' ] , raise_type = Scm . LocalException ) <EOL> except Scm . LocalException as abort_exc : <EOL> logger . debug ( '<STR_LIT>' ) <EOL> logger . debug ( traceback . format_exc ( abort_exc ) ) <EOL> raise e <EOL> def tag ( self , name , message = None ) : <EOL> self . _check_call ( [ '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' + ( message or '<STR_LIT>' ) , name ] , <EOL> raise_type = Scm . LocalException ) <EOL> self . push ( '<STR_LIT>' + name ) <EOL> def commit ( self , message ) : <EOL> self . _check_call ( [ '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' + message ] , raise_type = Scm . LocalException ) <EOL> def add ( self , * paths ) : <EOL> self . _check_call ( [ '<STR_LIT>' ] + list ( paths ) , raise_type = Scm . LocalException ) <EOL> def commit_date ( self , commit_reference ) : <EOL> return self . _check_output ( [ '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' , commit_reference ] , <EOL> raise_type = Scm . LocalException ) <EOL> def push ( self , * refs ) : <EOL> remote , merge = self . _get_upstream ( ) <EOL> self . _check_call ( [ '<STR_LIT>' , remote , merge ] + list ( refs ) , raise_type = Scm . RemoteException ) <EOL> def _get_upstream ( self ) : <EOL> """<STR_LIT>""" <EOL> if not self . _remote or not self . _branch : <EOL> branch = self . branch_name <EOL> if not branch : <EOL> raise Scm . LocalException ( '<STR_LIT>' ) <EOL> def get_local_config ( key ) : <EOL> value = self . _check_output ( [ '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' , key ] , <EOL> raise_type = Scm . LocalException ) <EOL> return value . strip ( ) <EOL> self . _remote = self . _remote or get_local_config ( '<STR_LIT>' . format ( branch ) ) <EOL> self . _branch = self . _branch or get_local_config ( '<STR_LIT>' . format ( branch ) ) <EOL> return self . _remote , self . _branch <EOL> def _check_call ( self , args , failure_msg = None , raise_type = None ) : <EOL> cmd = self . _create_git_cmdline ( args ) <EOL> self . _log_call ( cmd ) <EOL> result = subprocess . call ( cmd ) <EOL> self . _check_result ( cmd , result , failure_msg , raise_type ) <EOL> def _check_output ( self , args , failure_msg = None , raise_type = None , errors = '<STR_LIT:strict>' ) : <EOL> cmd = self . _create_git_cmdline ( args ) <EOL> self . _log_call ( cmd ) <EOL> process , out = self . _invoke ( cmd ) <EOL> self . _check_result ( cmd , process . returncode , failure_msg , raise_type ) <EOL> return self . _cleanse ( out , errors = errors ) <EOL> def _create_git_cmdline ( self , args ) : <EOL> return [ self . _gitcmd , '<STR_LIT>' + self . _gitdir , '<STR_LIT>' + self . _worktree ] + args <EOL> def _log_call ( self , cmd ) : <EOL> logger . debug ( '<STR_LIT>' + '<STR_LIT:U+0020>' . join ( cmd ) ) <EOL> def repo_reader ( self , rev ) : <EOL> return GitRepositoryReader ( self , rev ) <EOL> class GitRepositoryReader ( object ) : <EOL> """<STR_LIT>""" <EOL> def __init__ ( self , scm , rev ) : <EOL> self . scm = scm <EOL> self . rev = rev <EOL> self . _cat_file_process = None <EOL> self . _trees = { } <EOL> self . _realpath_cache = { '<STR_LIT:.>' : '<STR_LIT>' , '<STR_LIT>' : '<STR_LIT>' } <EOL> def _maybe_start_cat_file_process ( self ) : <EOL> if not self . _cat_file_process : <EOL> cmdline = self . scm . _create_git_cmdline ( [ '<STR_LIT>' , '<STR_LIT>' ] ) <EOL> self . _cat_file_process = subprocess . Popen ( cmdline , <EOL> stdin = subprocess . PIPE , stdout = subprocess . PIPE ) <EOL> class MissingFileException ( Exception ) : <EOL> def __init__ ( self , rev , relpath ) : <EOL> self . relpath = relpath <EOL> self . rev = rev <EOL> def __str__ ( self ) : <EOL> return "<STR_LIT>" . format ( self . relpath , self . rev ) <EOL> class IsDirException ( Exception ) : <EOL> def __init__ ( self , rev , relpath ) : <EOL> self . relpath = relpath <EOL> self . rev = rev <EOL> def __str__ ( self ) : <EOL> return "<STR_LIT>" . format ( self . relpath , self . rev ) <EOL> class NotADirException ( Exception ) : <EOL> def __init__ ( self , rev , relpath ) : <EOL> self . relpath = relpath <EOL> self . rev = rev <EOL> def __str__ ( self ) : <EOL> return "<STR_LIT>" . format ( self . relpath , self . rev ) <EOL> class SymlinkLoopException ( Exception ) : <EOL> def __init__ ( self , rev , relpath ) : <EOL> self . relpath = relpath <EOL> self . rev = rev <EOL> def __str__ ( self ) : <EOL> return "<STR_LIT>" . format ( self . relpath , self . rev ) <EOL> class ExternalSymlinkException ( Exception ) : <EOL> def __init__ ( self , rev , relpath ) : <EOL> self . relpath = relpath <EOL> self . rev = rev <EOL> def __str__ ( self ) : <EOL> return "<STR_LIT>" . format ( self . relpath , self . rev ) <EOL> class GitDiedException ( Exception ) : <EOL> pass <EOL> class UnexpectedGitObjectTypeException ( Exception ) : <EOL> pass <EOL> def _safe_realpath ( self , relpath ) : <EOL> try : <EOL> return self . _realpath ( relpath ) <EOL> except self . MissingFileException : <EOL> return None <EOL> except self . NotADirException : <EOL> return None <EOL> def _safe_read_object ( self , relpath , max_symlinks ) : <EOL> try : <EOL> return self . _read_object ( relpath , max_symlinks ) <EOL> except self . MissingFileException : <EOL> return None , relpath <EOL> except self . NotADirException : <EOL> return None , relpath <EOL> def exists ( self , relpath ) : <EOL> path = self . _safe_realpath ( relpath ) <EOL> return bool ( path ) <EOL> def isfile ( self , relpath ) : <EOL> path = self . _safe_realpath ( relpath ) <EOL> if path : <EOL> return not path . endswith ( '<STR_LIT:/>' ) <EOL> return False <EOL> def isdir ( self , relpath ) : <EOL> path = self . _safe_realpath ( relpath ) <EOL> if path : <EOL> return path . endswith ( '<STR_LIT:/>' ) <EOL> return False <EOL> def lstat ( self , relpath ) : <EOL> obj , _ = self . _safe_read_object ( relpath , max_symlinks = <NUM_LIT:0> ) <EOL> return obj <EOL> def readlink ( self , relpath ) : <EOL> if type ( self . lstat ( relpath ) ) != self . Symlink : <EOL> return None <EOL> obj , path_so_far = self . _safe_read_object ( relpath , max_symlinks = <NUM_LIT:1> ) <EOL> if obj == None : <EOL> return None <EOL> return path_so_far <EOL> class Symlink ( object ) : <EOL> def __init__ ( self , name , sha ) : <EOL> self . name = name <EOL> self . sha = sha <EOL> class Dir ( object ) : <EOL> def __init__ ( self , name , sha ) : <EOL> self . name = name <EOL> self . sha = sha <EOL> class File ( object ) : <EOL> def __init__ ( self , name , sha ) : <EOL> self . name = name <EOL> self . sha = sha <EOL> def listdir ( self , relpath ) : <EOL> """<STR_LIT>""" <EOL> path = self . _realpath ( relpath ) <EOL> if not path . endswith ( '<STR_LIT:/>' ) : <EOL> raise self . NotADirException ( self . rev , relpath ) <EOL> if path [ <NUM_LIT:0> ] == '<STR_LIT:/>' or path . startswith ( '<STR_LIT>' ) : <EOL> return os . listdir ( path ) <EOL> tree = self . _read_tree ( path [ : - <NUM_LIT:1> ] ) <EOL> return tree . keys ( ) <EOL> @ contextmanager <EOL> def open ( self , relpath ) : <EOL> """<STR_LIT>""" <EOL> path = self . _realpath ( relpath ) <EOL> if path . endswith ( '<STR_LIT:/>' ) : <EOL> raise self . IsDirException ( self . rev , relpath ) <EOL> if path . startswith ( '<STR_LIT>' ) or path [ <NUM_LIT:0> ] == '<STR_LIT:/>' : <EOL> yield open ( path , '<STR_LIT:rb>' ) <EOL> return <EOL> object_type , data = self . _read_object_from_repo ( rev = self . rev , relpath = path ) <EOL> if object_type == '<STR_LIT>' : <EOL> raise self . IsDirException ( self . rev , relpath ) <EOL> assert object_type == '<STR_LIT>' <EOL> yield StringIO . StringIO ( data ) <EOL> @ memoized_method <EOL> def _realpath ( self , relpath ) : <EOL> """<STR_LIT>""" <EOL> obj , path_so_far = self . _read_object ( relpath , MAX_SYMLINKS_IN_REALPATH ) <EOL> if isinstance ( obj , self . Symlink ) : <EOL> raise self . SymlinkLoopException ( self . rev , relpath ) <EOL> return path_so_far <EOL> def _read_object ( self , relpath , max_symlinks ) : <EOL> path_so_far = '<STR_LIT>' <EOL> components = list ( relpath . split ( os . path . sep ) ) <EOL> symlinks = <NUM_LIT:0> <EOL> while components : <EOL> component = components . pop ( <NUM_LIT:0> ) <EOL> if component == '<STR_LIT>' or component == '<STR_LIT:.>' : <EOL> continue <EOL> parent_tree = self . _read_tree ( path_so_far ) <EOL> parent_path = path_so_far <EOL> if path_so_far != '<STR_LIT>' : <EOL> path_so_far += '<STR_LIT:/>' <EOL> path_so_far += component <EOL> try : <EOL> obj = parent_tree [ component ] <EOL> except KeyError : <EOL> raise self . MissingFileException ( self . rev , relpath ) <EOL> if isinstance ( obj , self . File ) : <EOL> if components : <EOL> raise self . NotADirException ( self . rev , relpath ) <EOL> else : <EOL> return obj , path_so_far <EOL> elif isinstance ( obj , self . Dir ) : <EOL> if not components : <EOL> return obj , path_so_far + '<STR_LIT:/>' <EOL> elif isinstance ( obj , self . Symlink ) : <EOL> symlinks += <NUM_LIT:1> <EOL> if symlinks > max_symlinks : <EOL> return obj , path_so_far <EOL> object_type , path_data = self . _read_object_from_repo ( sha = obj . sha ) <EOL> assert object_type == '<STR_LIT>' <EOL> if path_data [ <NUM_LIT:0> ] == '<STR_LIT:/>' : <EOL> raise self . ExternalSymlinkException ( self . rev , relpath ) <EOL> link_to = os . path . normpath ( os . path . join ( parent_path , path_data ) ) <EOL> if link_to . startswith ( '<STR_LIT>' ) or link_to [ <NUM_LIT:0> ] == '<STR_LIT:/>' : <EOL> raise self . ExternalSymlinkException ( self . rev , relpath ) <EOL> components = link_to . split ( SLASH ) + components <EOL> path_so_far = '<STR_LIT>' <EOL> else : <EOL> raise self . UnexpectedGitObjectTypeException ( ) <EOL> return self . Dir ( '<STR_LIT>' , None ) , '<STR_LIT>' <EOL> def _fixup_dot_relative ( self , path ) : <EOL> """<STR_LIT>""" <EOL> if path . startswith ( '<STR_LIT>' ) : <EOL> return path [ <NUM_LIT:2> : ] <EOL> elif path == '<STR_LIT:.>' : <EOL> return '<STR_LIT>' <EOL> return path <EOL> def _read_tree ( self , path ) : <EOL> """<STR_LIT>""" <EOL> path = self . _fixup_dot_relative ( path ) <EOL> tree = self . _trees . get ( path ) <EOL> if tree : <EOL> return tree <EOL> tree = { } <EOL> object_type , tree_data = self . _read_object_from_repo ( rev = self . rev , relpath = path ) <EOL> assert object_type == '<STR_LIT>' <EOL> i = <NUM_LIT:0> <EOL> while i < len ( tree_data ) : <EOL> start = i <EOL> while tree_data [ i ] != '<STR_LIT:U+0020>' : <EOL> i += <NUM_LIT:1> <EOL> mode = tree_data [ start : i ] <EOL> i += <NUM_LIT:1> <EOL> start = i <EOL> while tree_data [ i ] != NUL : <EOL> i += <NUM_LIT:1> <EOL> name = tree_data [ start : i ] <EOL> sha = tree_data [ i + <NUM_LIT:1> : i + <NUM_LIT:1> + GIT_HASH_LENGTH ] . encode ( '<STR_LIT>' ) <EOL> i += <NUM_LIT:1> + GIT_HASH_LENGTH <EOL> if mode == '<STR_LIT>' : <EOL> tree [ name ] = self . Symlink ( name , sha ) <EOL> elif mode == '<STR_LIT>' : <EOL> tree [ name ] = self . Dir ( name , sha ) <EOL> else : <EOL> tree [ name ] = self . File ( name , sha ) <EOL> self . _trees [ path ] = tree <EOL> return tree <EOL> def _read_object_from_repo ( self , rev = None , relpath = None , sha = None ) : <EOL> """<STR_LIT>""" <EOL> if sha : <EOL> spec = sha + '<STR_LIT:\n>' <EOL> else : <EOL> assert rev is not None <EOL> assert relpath is not None <EOL> relpath = self . _fixup_dot_relative ( relpath ) <EOL> spec = '<STR_LIT>' . format ( rev , relpath ) <EOL> self . _maybe_start_cat_file_process ( ) <EOL> self . _cat_file_process . stdin . write ( spec ) <EOL> self . _cat_file_process . stdin . flush ( ) <EOL> header = None <EOL> while not header : <EOL> header = self . _cat_file_process . stdout . readline ( ) <EOL> if self . _cat_file_process . poll ( ) is not None : <EOL> raise self . GitDiedException ( "<STR_LIT>" . format ( spec ) ) <EOL> header = header . rstrip ( ) <EOL> parts = header . rsplit ( SPACE , <NUM_LIT:2> ) <EOL> if len ( parts ) == <NUM_LIT:2> : <EOL> assert parts [ <NUM_LIT:1> ] == '<STR_LIT>' <EOL> raise self . MissingFileException ( rev , relpath ) <EOL> _ , object_type , object_len = parts <EOL> blob = self . _cat_file_process . stdout . read ( int ( object_len ) ) <EOL> assert self . _cat_file_process . stdout . read ( <NUM_LIT:1> ) == '<STR_LIT:\n>' <EOL> assert len ( blob ) == int ( object_len ) <EOL> return object_type , blob <EOL> def __del__ ( self ) : <EOL> if self . _cat_file_process : <EOL> self . _cat_file_process . communicate ( ) </s>
