@ -68,6 +68,24 @@ import watchlists
__version__ = ' 2.0 '
# Traces for git push will be stored in a traces directory inside the
# depot_tools checkout.
DEPOT_TOOLS = os . path . dirname ( os . path . abspath ( __file__ ) )
TRACES_DIR = os . path . join ( DEPOT_TOOLS , ' traces ' )
# When collecting traces, Git hashes will be reduced to 6 characters to reduce
# the size after compression.
GIT_HASH_RE = re . compile ( r ' \ b([a-f0-9] {6} )[a-f0-9] {34} \ b ' , flags = re . I )
# Used to redact the cookies from the gitcookies file.
GITCOOKIES_REDACT_RE = re . compile ( r ' 1/.* ' )
TRACES_MESSAGE = (
' When filing a bug, be sure to include the traces found at: \n '
' %s .zip \n '
' Consider including the git config and gitcookies, \n '
' which we have packed for you at: \n '
' %s .zip \n ' )
COMMIT_BOT_EMAIL = ' commit-bot@chromium.org '
POSTUPSTREAM_HOOK = ' .git/hooks/post-cl-land '
DESCRIPTION_BACKUP_FILE = ' ~/.git_cl_description_backup '
@ -2478,6 +2496,83 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
else :
print ( ' OK, will keep Gerrit commit-msg hook in place. ' )
def _RunGitPushWithTraces ( self , change_desc , refspec , refspec_opts ) :
gclient_utils . safe_makedirs ( TRACES_DIR )
# Create a temporary directory to store traces in. Traces will be compressed
# and stored in a 'traces' dir inside depot_tools.
traces_dir = tempfile . mkdtemp ( )
trace_name = os . path . basename ( traces_dir )
traces_zip = os . path . join ( TRACES_DIR , trace_name + ' -traces ' )
# Create a temporary dir to store git config and gitcookies in. It will be
# compressed and stored next to the traces.
git_info_dir = tempfile . mkdtemp ( )
git_info_zip = os . path . join ( TRACES_DIR , trace_name + ' -git-info ' )
env = os . environ . copy ( )
env [ ' GIT_REDACT_COOKIES ' ] = ' o,SSO,GSSO_Uberproxy '
env [ ' GIT_TR2_EVENT ' ] = os . path . join ( traces_dir , ' tr2-event ' )
env [ ' GIT_TRACE_CURL ' ] = os . path . join ( traces_dir , ' trace-curl ' )
env [ ' GIT_TRACE_CURL_NO_DATA ' ] = ' 1 '
env [ ' GIT_TRACE_PACKET ' ] = os . path . join ( traces_dir , ' trace-packet ' )
try :
push_returncode = 0
before_push = time_time ( )
push_stdout = gclient_utils . CheckCallAndFilter (
[ ' git ' , ' push ' , self . GetRemoteUrl ( ) , refspec ] ,
env = env ,
print_stdout = True ,
# Flush after every line: useful for seeing progress when running as
# recipe.
filter_fn = lambda _ : sys . stdout . flush ( ) )
except subprocess2 . CalledProcessError as e :
push_returncode = e . returncode
DieWithError ( ' Failed to create a change. Please examine output above '
' for the reason of the failure. \n '
' Hint: run command below to diagnose common Git/Gerrit '
' credential problems: \n '
' git cl creds-check \n ' +
TRACES_MESSAGE % ( traces_zip , git_info_zip ) ,
change_desc )
finally :
execution_time = time_time ( ) - before_push
metrics . collector . add_repeated ( ' sub_commands ' , {
' command ' : ' git push ' ,
' execution_time ' : execution_time ,
' exit_code ' : push_returncode ,
' arguments ' : metrics_utils . extract_known_subcommand_args ( refspec_opts ) ,
} )
if push_returncode != 0 :
# Keep only the first 6 characters of the git hashes on the packet
# trace. This greatly decreases size after compression.
packet_traces = os . path . join ( traces_dir , ' trace-packet ' )
contents = gclient_utils . FileRead ( packet_traces )
gclient_utils . FileWrite (
packet_traces , GIT_HASH_RE . sub ( r ' \ 1 ' , contents ) )
shutil . make_archive ( traces_zip , ' zip ' , traces_dir )
# Collect and compress the git config and gitcookies.
git_config = RunGit ( [ ' config ' , ' -l ' ] )
gclient_utils . FileWrite (
os . path . join ( git_info_dir , ' git-config ' ) ,
git_config )
cookie_auth = gerrit_util . Authenticator . get ( )
if isinstance ( cookie_auth , gerrit_util . CookiesAuthenticator ) :
gitcookies_path = cookie_auth . get_gitcookies_path ( )
gitcookies = gclient_utils . FileRead ( gitcookies_path )
gclient_utils . FileWrite (
os . path . join ( git_info_dir , ' gitcookies ' ) ,
GITCOOKIES_REDACT_RE . sub ( ' REDACTED ' , gitcookies ) )
shutil . make_archive ( git_info_zip , ' zip ' , git_info_dir )
gclient_utils . rmtree ( git_info_dir )
gclient_utils . rmtree ( traces_dir )
return push_stdout
def CMDUploadChange ( self , options , git_diff_args , custom_cl_base , change ) :
""" Upload the current branch to Gerrit. """
if options . squash and options . no_squash :
@ -2727,30 +2822,7 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase):
' spaces not allowed in refspec: " %s " ' % refspec_suffix )
refspec = ' %s :refs/for/ %s %s ' % ( ref_to_push , branch , refspec_suffix )
try :
push_returncode = 0
before_push = time_time ( )
push_stdout = gclient_utils . CheckCallAndFilter (
[ ' git ' , ' push ' , self . GetRemoteUrl ( ) , refspec ] ,
print_stdout = True ,
# Flush after every line: useful for seeing progress when running as
# recipe.
filter_fn = lambda _ : sys . stdout . flush ( ) )
except subprocess2 . CalledProcessError as e :
push_returncode = e . returncode
DieWithError ( ' Failed to create a change. Please examine output above '
' for the reason of the failure. \n '
' Hint: run command below to diagnose common Git/Gerrit '
' credential problems: \n '
' git cl creds-check \n ' ,
change_desc )
finally :
metrics . collector . add_repeated ( ' sub_commands ' , {
' command ' : ' git push ' ,
' execution_time ' : time_time ( ) - before_push ,
' exit_code ' : push_returncode ,
' arguments ' : metrics_utils . extract_known_subcommand_args ( refspec_opts ) ,
} )
push_stdout = self . _RunGitPushWithTraces ( change_desc , refspec , refspec_opts )
if options . squash :
regex = re . compile ( r ' remote: \ s+https?://[ \ w \ - \ . \ + \ /#]*/( \ d+) \ s.* ' )