From 2d9135a9d254ec84f0790c99f81c3e0b7e712551 Mon Sep 17 00:00:00 2001 From: Jannik ZANDER Date: Sun, 1 May 2016 09:59:49 +0200 Subject: [PATCH] Add cygwin batch and other script files --- bat/cyg_batch.bat | 7 + bat/git_add.bat | 6 + bat/git_bd.bat | 6 + bat/git_blame.bat | 6 + bat/git_br.bat | 6 + bat/git_clean.bat | 6 + bat/git_co.bat | 13 + bat/git_co_file_head.bat | 6 + bat/git_com.bat | 6 + bat/git_commit_amend.bat | 6 + bat/git_difftool.bat | 21 + bat/git_difftool_headhat.bat | 6 + bat/git_logp.bat | 6 + bat/git_lola.bat | 6 + bat/git_rebase.bat | 14 + bat/git_reset.bat | 6 + bat/git_reset_file_head.bat | 6 + bat/git_review_diffpatch.bat | 13 + bat/git_review_download.bat | 15 + bat/git_review_finish.bat | 6 + bat/git_review_list.bat | 6 + bat/git_review_pushchange.bat | 6 + bat/git_stat.bat | 6 + bat/gitk.bat | 6 + bat/sync_gitcache.bat | 2 + bin/git-diffall.sh | 277 ++++++++ bin/git-difftool.sh | 4 + bin/git-editor.sh | 10 + bin/git-mergetool.sh | 16 + bin/git-proxy.sh | 5 + bin/git-review | 1219 +++++++++++++++++++++++++++++++++ 31 files changed, 1724 insertions(+) create mode 100644 bat/cyg_batch.bat create mode 100644 bat/git_add.bat create mode 100644 bat/git_bd.bat create mode 100644 bat/git_blame.bat create mode 100644 bat/git_br.bat create mode 100644 bat/git_clean.bat create mode 100644 bat/git_co.bat create mode 100644 bat/git_co_file_head.bat create mode 100644 bat/git_com.bat create mode 100644 bat/git_commit_amend.bat create mode 100644 bat/git_difftool.bat create mode 100644 bat/git_difftool_headhat.bat create mode 100644 bat/git_logp.bat create mode 100644 bat/git_lola.bat create mode 100644 bat/git_rebase.bat create mode 100644 bat/git_reset.bat create mode 100644 bat/git_reset_file_head.bat create mode 100644 bat/git_review_diffpatch.bat create mode 100644 bat/git_review_download.bat create mode 100644 bat/git_review_finish.bat create mode 100644 bat/git_review_list.bat create mode 100644 bat/git_review_pushchange.bat create mode 100644 bat/git_stat.bat create mode 100644 bat/gitk.bat create mode 100644 bat/sync_gitcache.bat create mode 100755 bin/git-diffall.sh create mode 100755 bin/git-difftool.sh create mode 100755 bin/git-editor.sh create mode 100755 bin/git-mergetool.sh create mode 100755 bin/git-proxy.sh create mode 100755 bin/git-review diff --git a/bat/cyg_batch.bat b/bat/cyg_batch.bat new file mode 100644 index 0000000..475cbc9 --- /dev/null +++ b/bat/cyg_batch.bat @@ -0,0 +1,7 @@ +@echo off + +cd %1 +set CHERE_INVOKING=1 +set CYGWIN=mintty +start cmd /c C:\cygwin\bin\bash.exe --login + diff --git a/bat/git_add.bat b/bat/git_add.bat new file mode 100644 index 0000000..570f48a --- /dev/null +++ b/bat/git_add.bat @@ -0,0 +1,6 @@ +@echo off + +cd %1 +set CHERE_INVOKING=1 +C:\cygwin\bin\bash.exe --login -i -c "git add %2" + diff --git a/bat/git_bd.bat b/bat/git_bd.bat new file mode 100644 index 0000000..690a944 --- /dev/null +++ b/bat/git_bd.bat @@ -0,0 +1,6 @@ +@echo off + +cd %1 +set CHERE_INVOKING=1 +C:\cygwin\bin\bash.exe --login -c "git brd %2" + diff --git a/bat/git_blame.bat b/bat/git_blame.bat new file mode 100644 index 0000000..082e627 --- /dev/null +++ b/bat/git_blame.bat @@ -0,0 +1,6 @@ +@echo off + +cd %1 +set CHERE_INVOKING=1 +C:\cygwin\bin\bash.exe --login -i -c "git blame %2" + diff --git a/bat/git_br.bat b/bat/git_br.bat new file mode 100644 index 0000000..1517259 --- /dev/null +++ b/bat/git_br.bat @@ -0,0 +1,6 @@ +@echo off + +cd %1 +set CHERE_INVOKING=1 +C:\cygwin\bin\bash.exe --login -c "git branch" + diff --git a/bat/git_clean.bat b/bat/git_clean.bat new file mode 100644 index 0000000..e6663ec --- /dev/null +++ b/bat/git_clean.bat @@ -0,0 +1,6 @@ +@echo off + +cd %1 +set CHERE_INVOKING=1 +C:\cygwin\bin\bash.exe --login -c "git clean -f -d" + diff --git a/bat/git_co.bat b/bat/git_co.bat new file mode 100644 index 0000000..fec4ab0 --- /dev/null +++ b/bat/git_co.bat @@ -0,0 +1,13 @@ +@echo off + +cd %1 +set CHERE_INVOKING=1 +IF %3 == 1 GOTO CREATE +C:\cygwin\bin\bash.exe --login -c "git co %2" +GOTO END + +:CREATE +C:\cygwin\bin\bash.exe --login -c "git co -b %2" + +:END + diff --git a/bat/git_co_file_head.bat b/bat/git_co_file_head.bat new file mode 100644 index 0000000..3a4805d --- /dev/null +++ b/bat/git_co_file_head.bat @@ -0,0 +1,6 @@ +@echo off + +cd %1 +set CHERE_INVOKING=1 +C:\cygwin\bin\bash.exe --login -i -c "git co HEAD -- %2" + diff --git a/bat/git_com.bat b/bat/git_com.bat new file mode 100644 index 0000000..85d1a1f --- /dev/null +++ b/bat/git_com.bat @@ -0,0 +1,6 @@ +@echo off + +cd %1 +set CHERE_INVOKING=1 +C:\cygwin\bin\bash.exe --login -c "git commit" + diff --git a/bat/git_commit_amend.bat b/bat/git_commit_amend.bat new file mode 100644 index 0000000..a959903 --- /dev/null +++ b/bat/git_commit_amend.bat @@ -0,0 +1,6 @@ +@echo off + +cd %1 +set CHERE_INVOKING=1 +C:\cygwin\bin\bash.exe --login -c "git commit --amend" + diff --git a/bat/git_difftool.bat b/bat/git_difftool.bat new file mode 100644 index 0000000..8ef72d1 --- /dev/null +++ b/bat/git_difftool.bat @@ -0,0 +1,21 @@ +@echo off + +cd %1 +set CHERE_INVOKING=1 + +IF NOT "%2"=="" GOTO P1 +C:\cygwin\bin\bash.exe --login -i -c "git diffall" +GOTO END + +:P1 +IF NOT "%3"=="" GOTO P2 +C:\cygwin\bin\bash.exe --login -i -c "git diffall %2" +GOTO END + +:P2 +C:\cygwin\bin\bash.exe --login -i -c "git diffall %2 %3" + +:END + + + diff --git a/bat/git_difftool_headhat.bat b/bat/git_difftool_headhat.bat new file mode 100644 index 0000000..ad72336 --- /dev/null +++ b/bat/git_difftool_headhat.bat @@ -0,0 +1,6 @@ +@echo off + +cd %1 +set CHERE_INVOKING=1 +C:\cygwin\bin\bash.exe --login -i -c "git diffall HEAD^" + diff --git a/bat/git_logp.bat b/bat/git_logp.bat new file mode 100644 index 0000000..67ed2f5 --- /dev/null +++ b/bat/git_logp.bat @@ -0,0 +1,6 @@ +@echo off + +cd %1 +set CHERE_INVOKING=1 +C:\cygwin\bin\bash.exe --login -i -c "git log -p -1 %2" + diff --git a/bat/git_lola.bat b/bat/git_lola.bat new file mode 100644 index 0000000..fb0d7fd --- /dev/null +++ b/bat/git_lola.bat @@ -0,0 +1,6 @@ +@echo off + +cd %1 +set CHERE_INVOKING=1 +C:\cygwin\bin\bash.exe --login -c "git lola" + diff --git a/bat/git_rebase.bat b/bat/git_rebase.bat new file mode 100644 index 0000000..95a7923 --- /dev/null +++ b/bat/git_rebase.bat @@ -0,0 +1,14 @@ +@echo off + +cd %1 +set CHERE_INVOKING=1 +IF %3 == 1 GOTO INTERACTIVE +C:\cygwin\bin\bash.exe --login -i -c "git rebase refs/tags/ste-m74x0_%2" +C:\cygwin\bin\bash.exe --login -i -c "git mergetool -y" +C:\cygwin\bin\bash.exe --login -i -c "git rebase --continue" +GOTO END + +:INTERACTIVE +C:\cygwin\bin\bash.exe --login -i -c "git rebase -i %2" + +:END \ No newline at end of file diff --git a/bat/git_reset.bat b/bat/git_reset.bat new file mode 100644 index 0000000..fad93db --- /dev/null +++ b/bat/git_reset.bat @@ -0,0 +1,6 @@ +@echo off + +cd %1 +set CHERE_INVOKING=1 +C:\cygwin\bin\bash.exe --login -i -c "git reset --hard" + diff --git a/bat/git_reset_file_head.bat b/bat/git_reset_file_head.bat new file mode 100644 index 0000000..f462b53 --- /dev/null +++ b/bat/git_reset_file_head.bat @@ -0,0 +1,6 @@ +@echo off + +cd %1 +set CHERE_INVOKING=1 +C:\cygwin\bin\bash.exe --login -i -c "git reset HEAD -- %2" + diff --git a/bat/git_review_diffpatch.bat b/bat/git_review_diffpatch.bat new file mode 100644 index 0000000..7f3e323 --- /dev/null +++ b/bat/git_review_diffpatch.bat @@ -0,0 +1,13 @@ +@echo off + +cd %1 +set CHERE_INVOKING=1 +IF "%4"=="" GOTO LATEST +C:\cygwin\bin\bash.exe --login -i -c "git review -R -m %2,%3-%4" + +GOTO END + +:LATEST +C:\cygwin\bin\bash.exe --login -i -c "git review -R -m %2,%3" + +:END diff --git a/bat/git_review_download.bat b/bat/git_review_download.bat new file mode 100644 index 0000000..9cdec36 --- /dev/null +++ b/bat/git_review_download.bat @@ -0,0 +1,15 @@ +@echo off + +cd %1 +set CHERE_INVOKING=1 +IF NOT "%3"=="" GOTO CUSTOMNAME +C:\cygwin\bin\bash.exe --login -i -c "git review -d %2" +GOTO END + +:CUSTOMNAME +C:\cygwin\bin\bash.exe --login -i -c "git review -b %3 -d %2" + +:END + + + diff --git a/bat/git_review_finish.bat b/bat/git_review_finish.bat new file mode 100644 index 0000000..0a5c844 --- /dev/null +++ b/bat/git_review_finish.bat @@ -0,0 +1,6 @@ +@echo off + +cd %1 +set CHERE_INVOKING=1 +C:\cygwin\bin\bash.exe --login -i -c "git review -f" + diff --git a/bat/git_review_list.bat b/bat/git_review_list.bat new file mode 100644 index 0000000..f613e0c --- /dev/null +++ b/bat/git_review_list.bat @@ -0,0 +1,6 @@ +@echo off + +cd %1 +set CHERE_INVOKING=1 +C:\cygwin\bin\bash.exe --login -i -c "git review -B -l" + diff --git a/bat/git_review_pushchange.bat b/bat/git_review_pushchange.bat new file mode 100644 index 0000000..ee3fdb4 --- /dev/null +++ b/bat/git_review_pushchange.bat @@ -0,0 +1,6 @@ +@echo off + +cd %1 +set CHERE_INVOKING=1 +C:\cygwin\bin\bash.exe --login -i -c "git review -c -p" + diff --git a/bat/git_stat.bat b/bat/git_stat.bat new file mode 100644 index 0000000..e7c25be --- /dev/null +++ b/bat/git_stat.bat @@ -0,0 +1,6 @@ +@echo off + +cd %1 +set CHERE_INVOKING=1 +C:\cygwin\bin\bash.exe --login -c "git status" + diff --git a/bat/gitk.bat b/bat/gitk.bat new file mode 100644 index 0000000..0fd3c49 --- /dev/null +++ b/bat/gitk.bat @@ -0,0 +1,6 @@ +@echo off + +cd %1 +set CHERE_INVOKING=1 +C:\cygwin\bin\bash.exe --login -i -c "gitk %2" + diff --git a/bat/sync_gitcache.bat b/bat/sync_gitcache.bat new file mode 100644 index 0000000..cddc8ce --- /dev/null +++ b/bat/sync_gitcache.bat @@ -0,0 +1,2 @@ +rem Cache in ~/gitcache +C:\cygwin\bin\bash.exe --login -i -c "cd ~/gitcache; repo sync" diff --git a/bin/git-diffall.sh b/bin/git-diffall.sh new file mode 100755 index 0000000..2ae11ea --- /dev/null +++ b/bin/git-diffall.sh @@ -0,0 +1,277 @@ +#!/bin/sh +# Copyright 2010 - 2012, Tim Henigan +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +# Perform a directory diff between commits in the repository using +# the external diff or merge tool specified in the user's config. + +USAGE='[--cached] [--copy-back] [-x|--extcmd=] {0,2} [-- *] + + --cached Compare to the index rather than the working tree. + + --copy-back Copy files back to the working tree when the diff + tool exits (in case they were modified by the + user). This option is only valid if the diff + compared with the working tree. + + -x= + --extcmd= Specify a custom command for viewing diffs. + git-diffall ignores the configured defaults and + runs $command $LOCAL $REMOTE when this option is + specified. Additionally, $BASE is set in the + environment. +' + +SUBDIRECTORY_OK=1 +. "$(git --exec-path)/git-sh-setup" + +TOOL_MODE=diff +. "$(git --exec-path)/git-mergetool--lib" + +merge_tool="$(get_merge_tool)" +if test -z "$merge_tool" +then + echo "Error: Either the 'diff.tool' or 'merge.tool' option must be set." + usage +fi + +start_dir=$(pwd) + +# All the file paths returned by the diff command are relative to the root +# of the working copy. So if the script is called from a subdirectory, it +# must switch to the root of working copy before trying to use those paths. +cdup=$(git rev-parse --show-cdup) && +cd "$cdup" || { + echo >&2 "Cannot chdir to $cdup, the toplevel of the working tree" + exit 1 +} + +# set up temp dir +tmp=$(perl -e 'use File::Temp qw(tempdir); + $t=tempdir("/tmp/git-diffall.XXXXX") or exit(1); + print $t') || exit 1 +#trap 'rm -rf "$tmp"' EXIT + +left= +right= +paths= +dashdash_seen= +compare_staged= +merge_base= +left_dir= +right_dir= +diff_tool= +copy_back= + +while test $# != 0 +do + case "$1" in + -h|--h|--he|--hel|--help) + usage + ;; + --cached) + compare_staged=1 + ;; + --copy-back) + copy_back=1 + ;; + -x|--e|--ex|--ext|--extc|--extcm|--extcmd) + if test $# = 1 + then + echo You must specify the tool for use with --extcmd + usage + else + diff_tool=$2 + shift + fi + ;; + --) + dashdash_seen=1 + ;; + -*) + echo Invalid option: "$1" + usage + ;; + *) + # could be commit, commit range or path limiter + case "$1" in + *...*) + left=${1%...*} + right=${1#*...} + merge_base=1 + ;; + *..*) + left=${1%..*} + right=${1#*..} + ;; + *) + if test -n "$dashdash_seen" + then + paths="$paths$1 " + elif test -z "$left" + then + left=$1 + elif test -z "$right" + then + right=$1 + else + paths="$paths$1 " + fi + ;; + esac + ;; + esac + shift +done + +# Determine the set of files which changed +if test -n "$left" && test -n "$right" +then + left_dir="cmt-$(git rev-parse --short $left)" + right_dir="cmt-$(git rev-parse --short $right)" + + if test -n "$compare_staged" + then + usage + elif test -n "$merge_base" + then + git diff --name-only "$left"..."$right" -- $paths >"$tmp/filelist" + else + git diff --name-only "$left" "$right" -- $paths >"$tmp/filelist" + fi +elif test -n "$left" +then + left_dir="cmt-$(git rev-parse --short $left)" + + if test -n "$compare_staged" + then + right_dir="staged" + git diff --name-only --cached "$left" -- $paths >"$tmp/filelist" + else + right_dir="working_tree" + git diff --name-only "$left" -- $paths >"$tmp/filelist" + fi +else + left_dir="HEAD" + + if test -n "$compare_staged" + then + right_dir="staged" + git diff --name-only --cached -- $paths >"$tmp/filelist" + else + right_dir="working_tree" + git diff --name-only -- $paths >"$tmp/filelist" + fi +fi + +# Exit immediately if there are no diffs +if test ! -s "$tmp/filelist" +then + exit 0 +fi + +if test -n "$copy_back" && test "$right_dir" != "working_tree" +then + echo "--copy-back is only valid when diff includes the working tree." + exit 1 +fi + +# Create the named tmp directories that will hold the files to be compared +mkdir -p "$tmp/$left_dir" "$tmp/$right_dir" + +# Populate the tmp/right_dir directory with the files to be compared +while read name +do + if test -n "$right" + then + ls_list=$(git ls-tree $right "$name") + if test -n "$ls_list" + then + mkdir -p "$tmp/$right_dir/$(dirname "$name")" + git show "$right":"$name" >"$tmp/$right_dir/$name" || true + fi + elif test -n "$compare_staged" + then + ls_list=$(git ls-files -- "$name") + if test -n "$ls_list" + then + mkdir -p "$tmp/$right_dir/$(dirname "$name")" + git show :"$name" >"$tmp/$right_dir/$name" + fi + else + if test -e "$name" + then + mkdir -p "$tmp/$right_dir/$(dirname "$name")" + cp "$name" "$tmp/$right_dir/$name" + fi + fi +done < "$tmp/filelist" + +# Populate the tmp/left_dir directory with the files to be compared +while read name +do + if test -n "$left" + then + ls_list=$(git ls-tree $left "$name") + if test -n "$ls_list" + then + mkdir -p "$tmp/$left_dir/$(dirname "$name")" + git show "$left":"$name" >"$tmp/$left_dir/$name" || true + fi + else + if test -n "$compare_staged" + then + ls_list=$(git ls-tree HEAD "$name") + if test -n "$ls_list" + then + mkdir -p "$tmp/$left_dir/$(dirname "$name")" + git show HEAD:"$name" >"$tmp/$left_dir/$name" + fi + else + mkdir -p "$tmp/$left_dir/$(dirname "$name")" + git show :"$name" >"$tmp/$left_dir/$name" + fi + fi +done < "$tmp/filelist" + +LOCAL="$tmp/$left_dir" +REMOTE="$tmp/$right_dir" + +if test -n "$diff_tool" +then + export BASE + eval $diff_tool '"$LOCAL"' '"$REMOTE"' +else + run_merge_tool "$merge_tool" false +fi + +# Copy files back to the working dir, if requested +if test -n "$copy_back" && test "$right_dir" = "working_tree" +then + cd "$start_dir" + git_top_dir=$(git rev-parse --show-toplevel) + find "$tmp/$right_dir" -type f | + while read file + do + cp "$file" "$git_top_dir/${file#$tmp/$right_dir/}" + done +fi \ No newline at end of file diff --git a/bin/git-difftool.sh b/bin/git-difftool.sh new file mode 100755 index 0000000..ea69a60 --- /dev/null +++ b/bin/git-difftool.sh @@ -0,0 +1,4 @@ +TOOL=$HOME/bin/vimdiff +ARG=$@ + +$TOOL $ARG diff --git a/bin/git-editor.sh b/bin/git-editor.sh new file mode 100755 index 0000000..d5afa9c --- /dev/null +++ b/bin/git-editor.sh @@ -0,0 +1,10 @@ +#!/bin/bash +if [[ -f $HOME/usr/bin/vim ]]; then + TOOL="${HOME}/usr/bin/vim" + ARG=$@ +elif [[ -f /usr/bin/vim ]]; then + TOOL="/usr/bin/vim" + ARG=$@ +fi + +$TOOL $ARG diff --git a/bin/git-mergetool.sh b/bin/git-mergetool.sh new file mode 100755 index 0000000..576c19f --- /dev/null +++ b/bin/git-mergetool.sh @@ -0,0 +1,16 @@ +#! /bin/bash +if [[ -f $HOME/bin/bcompare ]]; then + TOOL="${HOME}/bin/bcompare" + ARG="$(cygpath -w $1) $(cygpath -w $2) $(cygpath -w $3) $(cygpath -w $4)" +elif [[ -f /sw/stericsson/lud_tools/bcompare/3.3.5.15075/rh5/bin/bcompare ]]; then + TOOL=/sw/stericsson/lud_tools/bcompare/3.3.5.15075/rh5/bin/bcompare + ARG=$@ +elif [[ -f $HOME/usr/bin/vimdiff ]]; then + TOOL="${HOME}/usr/bin/vimdiff" + ARG=$@ +elif [[ -f /usr/bin/vimdiff ]]; then + TOOL="/usr/bin/vimdiff" + ARG=$@ +fi + +$TOOL $ARG diff --git a/bin/git-proxy.sh b/bin/git-proxy.sh new file mode 100755 index 0000000..5878c22 --- /dev/null +++ b/bin/git-proxy.sh @@ -0,0 +1,5 @@ + #!/bin/sh +exec connect-proxy -5 -R remote -S www-proxy.ericsson.se:3132 $* + + + diff --git a/bin/git-review b/bin/git-review new file mode 100755 index 0000000..9c1ba04 --- /dev/null +++ b/bin/git-review @@ -0,0 +1,1219 @@ +#!/usr/bin/env python +from __future__ import print_function + +COPYRIGHT = """\ +Copyright (C) 2011-2012 OpenStack LLC. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied. + +See the License for the specific language governing permissions and +limitations under the License.""" + +import datetime +import json +import os +import re +import shlex +import subprocess +import sys +import time + +if sys.version < '3': + import ConfigParser + import urllib + import urlparse + urlopen = urllib.urlopen + urlparse = urlparse.urlparse + do_input = raw_input +else: + import configparser as ConfigParser + import urllib.parse + import urllib.request + urlopen = urllib.request.urlopen + urlparse = urllib.parse.urlparse + do_input = input + +from distutils import version as du_version + +version = "1.22" + +VERBOSE = False +UPDATE = False +CONFIGDIR = os.path.expanduser("~/.config/git-review") +GLOBAL_CONFIG = "/etc/git-review/git-review.conf" +USER_CONFIG = os.path.join(CONFIGDIR, "git-review.conf") +PYPI_URL = "http://pypi.python.org/pypi/git-review/json" +PYPI_CACHE_TIME = 60 * 60 * 24 # 24 hours +DEFAULTS = dict(hostname='gerrit.lud.stericsson.com', port='29418', project=False, + defaultbranch='master', defaultremote="gerrit", + defaultrebase="0") + +_branch_name = None +_has_color = None +_no_color_support = False + + +class colors: + yellow = '\033[33m' + green = '\033[92m' + reset = '\033[0m' + + +class GitReviewException(Exception): + pass + + +class CommandFailed(GitReviewException): + + def __init__(self, *args): + Exception.__init__(self, *args) + (self.rc, self.output, self.argv, self.envp) = args + self.quickmsg = dict([ + ("argv", " ".join(self.argv)), + ("rc", self.rc), + ("output", self.output)]) + + def __str__(self): + return self.__doc__ + """ +The following command failed with exit code %(rc)d + "%(argv)s" +----------------------- +%(output)s +-----------------------""" % self.quickmsg + + +class ChangeSetException(GitReviewException): + + def __init__(self, e): + GitReviewException.__init__(self) + self.e = str(e) + + def __str__(self): + return self.__doc__ % self.e + + +def parse_review_number(review): + parts = review.split(',') + if len(parts) < 2: + parts.append(None) + return parts + + +def build_review_number(review, patchset): + if patchset is not None: + return '%s,%s' % (review, patchset) + return review + + +def run_command_status(*argv, **env): + if VERBOSE: + print(datetime.datetime.now(), "Running:", " ".join(argv)) + if len(argv) == 1: + argv = shlex.split(str(argv[0])) + newenv = os.environ + newenv.update(env) + p = subprocess.Popen(argv, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, env=newenv) + (out, nothing) = p.communicate() + out = out.decode('utf-8') + return (p.returncode, out.strip()) + + +def run_command(*argv, **env): + (rc, output) = run_command_status(*argv, **env) + return output + + +def run_command_exc(klazz, *argv, **env): + """Run command *argv, on failure raise 'klazz + + klass should be derived from CommandFailed + """ + (rc, output) = run_command_status(*argv, **env) + if rc != 0: + raise klazz(rc, output, argv, env) + return output + + +def update_latest_version(version_file_path): + """Cache the latest version of git-review for the upgrade check.""" + + if not os.path.exists(CONFIGDIR): + os.makedirs(CONFIGDIR) + + if os.path.exists(version_file_path) and not UPDATE: + if (time.time() - os.path.getmtime(version_file_path)) < 28800: + return + + latest_version = version + try: + latest_version = json.load(urlopen(PYPI_URL))['info']['version'] + except Exception: + pass + + with open(version_file_path, "w") as version_file: + version_file.write(latest_version) + + +def latest_is_newer(): + """Check if there is a new version of git-review.""" + + # Skip version check if distro package turns it off + if os.path.exists(GLOBAL_CONFIG): + config = dict(check=False) + configParser = ConfigParser.ConfigParser(config) + configParser.read(GLOBAL_CONFIG) + if not configParser.getboolean("updates", "check"): + return False + + version_file_path = os.path.join(CONFIGDIR, "latest-version") + update_latest_version(version_file_path) + + latest_version = None + with open(version_file_path, "r") as version_file: + latest_version = du_version.StrictVersion(version_file.read()) + if latest_version > du_version.StrictVersion(version): + return True + return False + + +def git_directories(): + """Determine (absolute git work directory path, .git subdirectory path).""" + cmd = ("git", "rev-parse", "--show-toplevel", "--git-dir") + out = run_command_exc(GitDirectoriesException, *cmd) + try: + return out.split() + except ValueError: + raise GitDirectoriesException(0, out, cmd, {}) + + +class GitDirectoriesException(CommandFailed): + "Cannot determine where .git directory is." + EXIT_CODE = 70 + + +class CustomScriptException(CommandFailed): + """Custom script execution failed.""" + EXIT_CODE = 71 + + +def run_custom_script(action): + """Get status and output of .git/hooks/$action-review or/and + ~/.config/hooks/$action-review if existing. + """ + returns = [] + script_file = "%s-review" % (action) + (top_dir, git_dir) = git_directories() + paths = [os.path.join(CONFIGDIR, "hooks", script_file), + os.path.join(git_dir, "hooks", script_file)] + for fpath in paths: + if os.path.isfile(fpath) and os.access(fpath, os.X_OK): + status, output = run_command_status(fpath) + returns.append((status, output, fpath)) + + for (status, output, path) in returns: + if status is not None and status != 0: + raise CustomScriptException(status, output, [path], {}) + elif output and VERBOSE: + print("script %s output is:" % (path)) + print(output) + + +def git_config_get_value(section, option, default=None): + try: + return run_command_exc(GitConfigException, + "git", "config", + "--get", + "%s.%s" % (section, option)).strip() + except GitConfigException as exc: + if exc.rc == 1: + return default + raise + + +class GitConfigException(CommandFailed): + """Git config value retrieval failed.""" + EXIT_CODE = 128 + + +class CannotInstallHook(CommandFailed): + "Problems encountered installing commit-msg hook" + EXIT_CODE = 2 + + +def set_hooks_commit_msg(remote, target_file): + """Install the commit message hook if needed.""" + + # Create the hooks directory if it's not there already + hooks_dir = os.path.dirname(target_file) + if not os.path.isdir(hooks_dir): + os.mkdir(hooks_dir) + + (hostname, username, port, project_name) = \ + parse_git_show(remote, "Push") + + if not os.path.exists(target_file) or UPDATE: + if VERBOSE: + print("Fetching commit hook from: scp://%s:%s" % (hostname, port)) + if port is not None: + port = "-P %s" % port + else: + port = "" + if username is None: + userhost = hostname + else: + userhost = "%s@%s" % (username, hostname) + run_command_exc( + CannotInstallHook, + "scp", port, + userhost + ":hooks/commit-msg", + target_file) + + if not os.access(target_file, os.X_OK): + os.chmod(target_file, os.path.stat.S_IREAD | os.path.stat.S_IEXEC) + + +def test_remote(username, hostname, port, project): + """Tests that a possible gerrit remote works.""" + + if port is not None: + port = "-p %s" % port + else: + port = "" + if username is None: + userhost = hostname + else: + userhost = "%s@%s" % (username, hostname) + + (status, ssh_output) = run_command_status( + "ssh", "-x", port, userhost, + "gerrit", "ls-projects") + + if status == 0: + if VERBOSE: + print("%s@%s:%s worked." % (username, hostname, port)) + return True + else: + if VERBOSE: + print("%s@%s:%s did not work." % (username, hostname, port)) + return False + + +def make_remote_url(username, hostname, port, project): + """Builds a gerrit remote URL.""" + if username is None: + return "ssh://%s:%s/%s" % (hostname, port, project) + else: + return "ssh://%s@%s:%s/%s" % (username, hostname, port, project) + + +def add_remote(hostname, port, project, remote): + """Adds a gerrit remote.""" + asked_for_username = False + + username = os.getenv("USERNAME") + if not username: + username = git_config_get_value("gitreview", "username") + if not username: + username = os.getenv("USER") + if port is None: + port = 29418 + + remote_url = make_remote_url(username, hostname, port, project) + if VERBOSE: + print("No remote set, testing %s" % remote_url) + if not test_remote(username, hostname, port, project): + print("Could not connect to gerrit.") + username = do_input("Enter your gerrit username: ") + remote_url = make_remote_url(username, hostname, port, project) + print("Trying again with %s" % remote_url) + if not test_remote(username, hostname, port, project): + raise Exception("Could not connect to gerrit at %s" % remote_url) + asked_for_username = True + + print("Creating a git remote called \"%s\" that maps to:" % remote) + print("\t%s" % remote_url) + cmd = "git remote add -f %s %s" % (remote, remote_url) + (status, remote_output) = run_command_status(cmd) + + if status != 0: + raise Exception("Error running %s" % cmd) + + if asked_for_username: + print() + print("This repository is now set up for use with git-review.") + print("You can set the default username for future repositories with:") + print(' git config --global --add gitreview.username "%s"' % username) + print() + + +def parse_git_show(remote, verb): + fetch_url = "" + for line in run_command("git remote show -n %s" % remote).split("\n"): + if line.strip().startswith("%s" % verb): + fetch_url = line.split()[2] + + parsed_url = urlparse(fetch_url) + project_name = parsed_url.path.lstrip("/") + if project_name.endswith(".git"): + project_name = project_name[:-4] + + hostname = parsed_url.netloc + username = None + port = parsed_url.port + + if VERBOSE: + print("Found origin %s URL:" % verb, fetch_url) + + # Workaround bug in urlparse on OSX + if parsed_url.scheme == "ssh" and parsed_url.path[:2] == "//": + hostname = parsed_url.path[2:].split("/")[0] + + if "@" in hostname: + (username, hostname) = hostname.split("@") + if ":" in hostname: + (hostname, port) = hostname.split(":") + + # Is origin an ssh location? Let's pull more info + if parsed_url.scheme == "ssh" and port is None: + port = 22 + + return (hostname, username, str(port), project_name) + + +def check_color_support(): + global _has_color + global _no_color_support + if _has_color is None: + test_command = "git log --color=never --oneline HEAD^1..HEAD" + (status, output) = run_command_status(test_command) + if status == 0: + _has_color = True + else: + _has_color = False + + if _no_color_support: + _has_color = False + + return _has_color + + +def get_config(config_file=None): + """Generate the configuration map by starting with some built-in defaults + and then loading GLOBAL_CONFIG, USER_CONFIG, and a repository-specific + .gitreview file, if they exist. In case of conflict, the configuration file + with the narrowest scope wins. + """ + config = DEFAULTS.copy() + for filename in (GLOBAL_CONFIG, USER_CONFIG, config_file): + if filename is not None and os.path.exists(filename): + config.update(load_config_file(filename)) + return config + + +def load_config_file(config_file): + """Load configuration options from a file.""" + configParser = ConfigParser.ConfigParser() + configParser.read(config_file) + options = { + 'hostname': 'host', + 'port': 'port', + 'project': 'project', + 'defaultbranch': 'defaultbranch', + 'defaultremote': 'defaultremote', + 'defaultrebase': 'defaultrebase', + } + config = {} + for config_key, option_name in options.items(): + if configParser.has_option('gerrit', option_name): + config[config_key] = configParser.get('gerrit', option_name) + return config + + +def update_remote(remote): + cmd = "git remote update %s" % remote + (status, output) = run_command_status(cmd) + if VERBOSE: + print(output) + if status != 0: + print("Problem running '%s'" % cmd) + if not VERBOSE: + print(output) + return False + return True + + +def check_remote(branch, remote, hostname, port, project): + """Check that a Gerrit Git remote repo exists, if not, set one.""" + + has_color = check_color_support() + if has_color: + color_never = "--color=never" + else: + color_never = "" + + if remote in run_command("git remote").split("\n"): + + remotes = run_command("git branch -a %s" % color_never).split("\n") + for current_remote in remotes: + if (current_remote.strip() == "remotes/%s/%s" % (remote, branch) + and not UPDATE): + return + # We have the remote, but aren't set up to fetch. Fix it + if VERBOSE: + print("Setting up gerrit branch tracking for better rebasing") + update_remote(remote) + return + + if hostname is False or port is False or project is False: + # This means there was no .gitreview file + print("No '.gitreview' file found in this repository.") + print("We don't know where your gerrit is. Please manually create ") + print("a remote named \"%s\" and try again." % remote) + sys.exit(1) + + # Gerrit remote not present, try to add it + try: + add_remote(hostname, port, project, remote) + except Exception: + print(sys.exc_info()[2]) + print("We don't know where your gerrit is. Please manually create ") + print("a remote named \"%s\" and try again." % remote) + raise + + +def rebase_changes(branch, remote, interactive=True): + + remote_branch = "remotes/%s/%s" % (remote, branch) + + if not update_remote(remote): + return False + + if interactive: + cmd = "git rebase -i %s" % remote_branch + else: + cmd = "git rebase %s" % remote_branch + + (status, output) = run_command_status(cmd, GIT_EDITOR='true') + if status != 0: + print("Errors running %s" % cmd) + if interactive: + print(output) + return False + return True + + +def undo_rebase(): + cmd = "git reset --hard ORIG_HEAD" + (status, output) = run_command_status(cmd) + if status != 0: + print("Errors running %s" % cmd) + print(output) + return False + return True + + +def get_branch_name(target_branch): + global _branch_name + if _branch_name is not None: + return _branch_name + _branch_name = None + has_color = check_color_support() + if has_color: + color_never = "--color=never" + else: + color_never = "" + for branch in run_command("git branch %s" % color_never).split("\n"): + if branch.startswith('*'): + _branch_name = branch.split()[1].strip() + if _branch_name == "(no": + _branch_name = target_branch + return _branch_name + + +def assert_one_change(remote, branch, yes, have_hook): + has_color = check_color_support() + if has_color: + color = git_config_get_value("color", "ui") + if color is None: + color = "auto" + else: + color = color.lower() + if (color == "" or color == "true"): + color = "auto" + elif color == "false": + color = "never" + elif color == "auto": + # Python is not a tty, we have to force colors + color = "always" + use_color = "--color=%s" % color + else: + use_color = "" + cmd = "git log %s --decorate --oneline HEAD --not remotes/%s/%s --" % ( + use_color, remote, branch) + (status, output) = run_command_status(cmd) + if status != 0: + print("Had trouble running %s" % cmd) + print(output) + sys.exit(1) + output_lines = len(output.split("\n")) + if output_lines == 1 and not have_hook: + print("Your change was committed before the commit hook was installed") + print("Amending the commit to add a gerrit change id") + run_command("git commit --amend", GIT_EDITOR='true') + elif output_lines == 0: + print("No changes between HEAD and %s/%s." % (remote, branch)) + print("Submitting for review would be pointless.") + sys.exit(1) + elif output_lines > 1: + if not yes: + print("You have more than one commit" + " that you are about to submit.") + print("The outstanding commits are:\n\n%s\n" % output) + print("Is this really what you meant to do?") + yes_no = do_input("Type 'yes' to confirm: ") + if yes_no.lower().strip() != "yes": + print("Aborting.") + print("Please rebase/squash your changes and try again") + sys.exit(1) + + +def use_topic(why, topic): + """Inform the user about why a particular topic has been selected.""" + if VERBOSE: + print(why % ('"%s"' % topic,)) + return topic + + +def get_topic(target_branch): + + branch_name = get_branch_name(target_branch) + + branch_parts = branch_name.split("/") + if len(branch_parts) >= 3 and branch_parts[0] == "review": + return use_topic("Using change number %s " + "for the topic of the change submitted", + "/".join(branch_parts[2:])) + + log_output = run_command("git log HEAD^1..HEAD") + bug_re = r'\b([Bb]ug|[Ll][Pp])\s*[#:]?\s*(\d+)' + + match = re.search(bug_re, log_output) + if match is not None: + return use_topic("Using bug number %s " + "for the topic of the change submitted", + "bug/%s" % match.group(2)) + + bp_re = r'\b([Bb]lue[Pp]rint|[Bb][Pp])\s*[#:]?\s*([0-9a-zA-Z-_]+)' + match = re.search(bp_re, log_output) + if match is not None: + return use_topic("Using blueprint number %s " + "for the topic of the change submitted", + "bp/%s" % match.group(2)) + + return use_topic("Using local branch name %s " + "for the topic of the change submitted", + branch_name) + + +class CannotQueryOpenChangesets(CommandFailed): + "Cannot fetch review information from gerrit" + EXIT_CODE = 32 + + +class CannotParseOpenChangesets(ChangeSetException): + "Cannot parse JSON review information from gerrit" + EXIT_CODE = 33 + + +def list_reviews(remote): + + (hostname, username, port, project_name) = \ + parse_git_show(remote, "Push") + + if port is not None: + port = "-p %s" % port + else: + port = "" + if username is None: + userhost = hostname + else: + userhost = "%s@%s" % (username, hostname) + + review_info = None + output = run_command_exc( + CannotQueryOpenChangesets, + "ssh", "-x", port, userhost, + "gerrit", "query", + "--current-patch-set --format=JSON project:%s status:open reviewer:self" % project_name) + + review_list = [] + review_field_width = {} + REVIEW_FIELDS = ('number', 'currentPatchSet', 'branch', 'subject') + FIELDS = range(0, len(REVIEW_FIELDS)) + if check_color_support(): + review_field_color = (colors.yellow, colors.yellow, colors.green, "") + color_reset = colors.reset + else: + review_field_color = ("", "", "", "") + color_reset = "" + review_field_width = [0, 0, 0, 0] + review_field_format = ["%*s", "%*s", "%*s", "%*s"] + review_field_justify = [+1, +1, +1, -1] # -1 is justify to right + + for line in output.split("\n"): + # Warnings from ssh wind up in this output + if line[0] != "{": + print(line) + continue + try: + review_info = json.loads(line) + except Exception: + if VERBOSE: + print(output) + raise(CannotParseOpenChangesets, sys.exc_info()[1]) + + if 'type' in review_info: + break + + tempPS = review_info['currentPatchSet'] + appPS = tempPS['approvals'] + appValue = '-'; + for appLine in appPS: + appBy = appLine['by'] + appUser = appBy['username'] + if appUser == username: + appValue = appLine['value'] + review_info['currentPatchSet'] = tempPS['number'] + ' ' + appValue + + review_list.append([review_info[f] for f in REVIEW_FIELDS]) + for i in FIELDS: + review_field_width[i] = max( + review_field_width[i], + len(review_info[REVIEW_FIELDS[i]]) + ) + + review_field_format = " ".join([ + review_field_color[i] + + review_field_format[i] + + color_reset + for i in FIELDS]) + + review_field_width = [ + review_field_width[i] * review_field_justify[i] + for i in FIELDS] + for review_value in review_list: + # At this point we have review_field_format + # like "%*s %*s %*s" and we need to supply + # (width1, value1, width2, value2, ...) tuple to print + # It's easy to zip() widths with actual values, + # but we need to flatten the resulting + # ((width1, value1), (width2, value2), ...) map. + formatted_fields = [] + for (width, value) in zip(review_field_width, review_value): + formatted_fields.extend([width, value]) + print(review_field_format % tuple(formatted_fields)) + print("Found %d items for review" % review_info['rowCount']) + + return 0 + + +class CannotQueryPatchSet(CommandFailed): + "Cannot query patchset information" + EXIT_CODE = 34 + + +class ReviewInformationNotFound(ChangeSetException): + "Could not fetch review information for change %s" + EXIT_CODE = 35 + + +class ReviewNotFound(ChangeSetException): + "Gerrit review %s not found" + EXIT_CODE = 36 + + +class PatchSetGitFetchFailed(CommandFailed): + """Cannot fetch patchset contents + +Does specified change number belong to this project? +""" + EXIT_CODE = 37 + + +class PatchSetNotFound(ChangeSetException): + "Review patchset %s not found" + EXIT_CODE = 38 + + +class CheckoutNewBranchFailed(CommandFailed): + "Cannot checkout to new branch" + EXIT_CODE = 64 + + +class CheckoutExistingBranchFailed(CommandFailed): + "Cannot checkout existing branch" + EXIT_CODE = 65 + + +class ResetHardFailed(CommandFailed): + "Failed to hard reset downloaded branch" + EXIT_CODE = 66 + + +def fetch_review(review, masterbranch, remote): + + (hostname, username, port, project_name) = \ + parse_git_show(remote, "Push") + + if port is not None: + port = "-p %s" % port + else: + port = "" + if username is None: + userhost = hostname + else: + userhost = "%s@%s" % (username, hostname) + + review_arg = review + patchset_opt = '--current-patch-set' + + review, patchset_number = parse_review_number(review) + if patchset_number is not None: + patchset_opt = '--patch-sets' + + review_info = None + output = run_command_exc( + CannotQueryPatchSet, + "ssh", "-x", port, userhost, + "gerrit", "query", + "--format=JSON %s change:%s" % (patchset_opt, review)) + + review_jsons = output.split("\n") + found_review = False + for review_json in review_jsons: + try: + review_info = json.loads(review_json) + found_review = True + except Exception: + pass + if found_review: + break + if not found_review: + if VERBOSE: + print(output) + raise ReviewInformationNotFound(review) + + try: + if patchset_number is None: + refspec = review_info['currentPatchSet']['ref'] + else: + refspec = [ps for ps + in review_info['patchSets'] + if ps['number'] == patchset_number][0]['ref'] + except IndexError: + raise PatchSetNotFound(review_arg) + except KeyError: + raise ReviewNotFound(review) + + try: + topic = review_info['topic'] + if topic == masterbranch: + topic = review + except KeyError: + topic = review + try: + author = re.sub('\W+', '_', review_info['owner']['name']).lower() + except KeyError: + author = 'unknown' + + if patchset_number is None: + branch_name = "review/%s/%s" % (author, topic) + else: + branch_name = "review/%s/%s-patch%s" % (author, topic, patchset_number) + + print("Downloading %s from gerrit" % refspec) + run_command_exc(PatchSetGitFetchFailed, + "git", "fetch", remote, refspec) + return branch_name + + +def checkout_review(branch_name): + """Checkout a newly fetched (FETCH_HEAD) change + into a branch + """ + + try: + run_command_exc(CheckoutNewBranchFailed, + "git", "checkout", "-b", + branch_name, "FETCH_HEAD") + + except CheckoutNewBranchFailed as e: + if re.search("already exists\.?", e.output): + print("Branch already exists - reusing") + run_command_exc(CheckoutExistingBranchFailed, + "git", "checkout", branch_name) + run_command_exc(ResetHardFailed, + "git", "reset", "--hard", "FETCH_HEAD") + else: + raise + + print("Switched to branch \"%s\"" % branch_name) + + +class PatchSetGitCherrypickFailed(CommandFailed): + "There was a problem applying changeset contents to the current branch." + EXIT_CODE = 69 + + +def cherrypick_review(option=None): + cmd = ["git", "cherry-pick"] + if option: + cmd.append(option) + cmd.append("FETCH_HEAD") + print(run_command_exc(PatchSetGitCherrypickFailed, *cmd)) + + +class CheckoutBackExistingBranchFailed(CommandFailed): + "Cannot switch back to existing branch" + EXIT_CODE = 67 + + +class DeleteBranchFailed(CommandFailed): + "Failed to delete branch" + EXIT_CODE = 68 + + +class InvalidPatchsetsToCompare(GitReviewException): + def __init__(self, patchsetA, patchsetB): + Exception.__init__( + self, + "Invalid patchsets for comparison specified (old=%s,new=%s)" % ( + patchsetA, + patchsetB)) + EXIT_CODE = 39 + + +def compare_review(review_spec, branch, remote, rebase=False): + new_ps = None # none means latest + + if '-' in review_spec: + review_spec, new_ps = review_spec.split('-') + review, old_ps = parse_review_number(review_spec) + + if old_ps is None or old_ps == new_ps: + raise InvalidPatchsetsToCompare(old_ps, new_ps) + + old_review = build_review_number(review, old_ps) + new_review = build_review_number(review, new_ps) + + old_branch = fetch_review(old_review, branch, remote) + checkout_review(old_branch) + + if rebase: + print('Rebasing %s' % old_branch) + rebase = rebase_changes(branch, remote, False) + if not rebase: + print('Skipping rebase because of conflicts') + run_command_exc(CommandFailed, 'git', 'rebase', '--abort') + + new_branch = fetch_review(new_review, branch, remote) + checkout_review(new_branch) + + if rebase: + print('Rebasing also %s' % new_branch) + if not rebase_changes(branch, remote, False): + print("Rebasing of the new branch failed, " + "diff can be messed up (use -R to not rebase at all)!") + run_command_exc(CommandFailed, 'git', 'rebase', '--abort') + + subprocess.check_call(['git', 'difftool', old_branch]) + + +def finish_branch(target_branch): + local_branch = get_branch_name(target_branch) + if VERBOSE: + print("Switching back to '%s' and deleting '%s'" % (target_branch, + local_branch)) + run_command_exc(CheckoutBackExistingBranchFailed, + "git", "checkout", target_branch) + print("Switched to branch '%s'" % target_branch) + + run_command_exc(DeleteBranchFailed, + "git", "branch", "-D", local_branch) + print("Deleted branch '%s'" % local_branch) + + +def convert_bool(one_or_zero): + "Return a bool on a one or zero string." + return one_or_zero in ["1", "true", "True"] + + +def print_exit_message(status, needs_update): + + if needs_update: + print(""" +*********************************************************** +A new version of git-review is available on PyPI. Please +update your copy with: + + pip install -U git-review + +to ensure proper behavior with gerrit. Thanks! +*********************************************************** +""") + sys.exit(status) + + +def main(): + global _no_color_support + usage = "git review [OPTIONS] ... [BRANCH]" + + import argparse + + class DownloadFlag(argparse.Action): + """Additional option parsing: store value in 'dest', but + at the same time set one of the flag options to True + """ + def __call__(self, parser, namespace, values, option_string=None): + setattr(namespace, self.dest, values) + setattr(namespace, self.const, True) + + parser = argparse.ArgumentParser(usage=usage, description=COPYRIGHT) + + parser.add_argument("-t", "--topic", dest="topic", + help="Topic to submit branch to") + parser.add_argument("-D", "--draft", dest="draft", action="store_true", + help="Submit review as a draft") + parser.add_argument("-c", "--compatible", dest="compatible", + action="store_true", + help="Push change to refs/for/* for compatibility " + "with Gerrit versions < 2.3. Ignored if " + "-D/--draft is used.") + parser.add_argument("-n", "--dry-run", dest="dry", action="store_true", + help="Don't actually submit the branch for review") + parser.add_argument("-i", "--new-changeid", dest="regenerate", + action="store_true", + help="Regenerate Change-id before submitting") + parser.add_argument("-r", "--remote", dest="remote", + help="git remote to use for gerrit") + parser.add_argument("-R", "--no-rebase", dest="rebase", + action="store_false", + help="Don't rebase changes before submitting.") + parser.add_argument("-F", "--force-rebase", dest="force_rebase", + action="store_true", + help="Force rebase even when not needed.") + parser.add_argument("-B", "--no-color", dest="no_color_support", + action="store_true", + help="No color support.") + + + fetch = parser.add_mutually_exclusive_group() + fetch.set_defaults(download=False, compare=False, cherrypickcommit=False, + cherrypickindicate=False, cherrypickonly=False) + fetch.add_argument("-d", "--download", dest="changeidentifier", + action=DownloadFlag, metavar="CHANGE", + const="download", + help="Download the contents of an existing gerrit " + "review into a branch") + fetch.add_argument("-x", "--cherrypick", dest="changeidentifier", + action=DownloadFlag, metavar="CHANGE", + const="cherrypickcommit", + help="Apply the contents of an existing gerrit " + "review onto the current branch and commit " + "(cherry pick; not recommended in most " + "situations)") + fetch.add_argument("-X", "--cherrypickindicate", dest="changeidentifier", + action=DownloadFlag, metavar="CHANGE", + const="cherrypickindicate", + help="Apply the contents of an existing gerrit " + "review onto the current branch and commit, " + "indicating its origin") + fetch.add_argument("-N", "--cherrypickonly", dest="changeidentifier", + action=DownloadFlag, metavar="CHANGE", + const="cherrypickonly", + help="Apply the contents of an existing gerrit " + "review to the working directory and prepare " + "for commit") + fetch.add_argument("-m", "--compare", dest="changeidentifier", + action=DownloadFlag, metavar="CHANGE,PS[-NEW_PS]", + const="compare", + help="Download specified and latest (or NEW_PS) " + "patchsets of an existing gerrit review into " + "a branches, rebase on master " + "(skipped on conflicts or when -R is specified) " + "and show their differences") + + parser.add_argument("-u", "--update", dest="update", action="store_true", + help="Force updates from remote locations") + parser.add_argument("-s", "--setup", dest="setup", action="store_true", + help="Just run the repo setup commands but don't " + "submit anything") + parser.add_argument("-f", "--finish", dest="finish", action="store_true", + help="Close down this branch and switch back to " + "master on successful submission") + parser.add_argument("-l", "--list", dest="list", action="store_true", + help="List available reviews for the current project") + parser.add_argument("-y", "--yes", dest="yes", action="store_true", + help="Indicate that you do, in fact, understand if " + "you are submitting more than one patch") + parser.add_argument("-v", "--verbose", dest="verbose", action="store_true", + help="Output more information about what's going on") + parser.add_argument("--no-custom-script", dest="custom_script", + action="store_false", default=True, + help="Do not run custom scripts.") + parser.add_argument("--license", dest="license", action="store_true", + help="Print the license and exit") + parser.add_argument("--version", action="version", + version='%s version %s' % + (os.path.split(sys.argv[0])[-1], version)) + parser.add_argument("branch", nargs="?") + + try: + (top_dir, git_dir) = git_directories() + except GitDirectoriesException: + if sys.argv[1:] in ([], ['-h'], ['--help']): + parser.print_help() + sys.exit(1) + raise + +# config = get_config(os.path.join(top_dir, ".gitreview")) + config = DEFAULTS.copy() + cur_proj = run_command("git remote -v") + import re + m = re.search('(?<=29418\/)(\S+)', cur_proj) + config['project'] = m.group(0) + + + defaultrebase = convert_bool( + git_config_get_value("gitreview", "rebase", + default=str(config['defaultrebase']))) + parser.set_defaults(dry=False, + draft=False, + rebase=defaultrebase, + verbose=False, + update=False, + setup=False, + list=False, + yes=False, + branch=config['defaultbranch'], + remote=config['defaultremote']) + options = parser.parse_args() + + if options.license: + print(COPYRIGHT) + sys.exit(0) + + branch = options.branch + global VERBOSE + global UPDATE + VERBOSE = options.verbose + UPDATE = options.update + remote = options.remote + yes = options.yes + status = 0 + + needs_update = latest_is_newer() + check_remote(branch, remote, + config['hostname'], config['port'], config['project']) + + if options.no_color_support: + _no_color_support = True + + if options.changeidentifier: + if options.compare: + compare_review(options.changeidentifier, + branch, remote, options.rebase) + return + local_branch = fetch_review(options.changeidentifier, branch, remote) + if options.download: + checkout_review(local_branch) + else: + if options.cherrypickcommit: + cherrypick_review() + elif options.cherrypickonly: + cherrypick_review("-n") + if options.cherrypickindicate: + cherrypick_review("-x") + return + elif options.list: + list_reviews(remote) + return + + if options.custom_script: + run_custom_script("pre") + + hook_file = os.path.join(git_dir, "hooks", "commit-msg") + have_hook = os.path.exists(hook_file) and os.access(hook_file, os.X_OK) + + if not have_hook: + set_hooks_commit_msg(remote, hook_file) + + if options.setup: + if options.finish and not options.dry: + finish_branch(branch) + return + + if options.rebase: + if not rebase_changes(branch, remote): + print_exit_message(1, needs_update) + if not options.force_rebase and not undo_rebase(): + print_exit_message(1, needs_update) + assert_one_change(remote, branch, yes, have_hook) + + ref = "publish" + + if options.draft: + ref = "drafts" + if options.custom_script: + run_custom_script("draft") + elif options.compatible: + ref = "for" + + cmd = "git push %s HEAD:refs/%s/%s" % (remote, ref, branch) + topic = options.topic or get_topic(branch) + if topic != branch: + cmd += "/%s" % topic + if options.regenerate: + print("Amending the commit to regenerate the change id\n") + regenerate_cmd = "git commit --amend" + if options.dry: + print("\tGIT_EDITOR=\"sed -i -e '/^Change-Id:/d'\" %s\n" % + regenerate_cmd) + else: + run_command(regenerate_cmd, + GIT_EDITOR="sed -i -e " + "'/^Change-Id:/d'") + + if options.dry: + print("Please use the following command " + "to send your commits to review:\n") + print("\t%s\n" % cmd) + else: + (status, output) = run_command_status(cmd) + print(output) + + if options.finish and not options.dry and status == 0: + finish_branch(branch) + return + + if options.custom_script: + run_custom_script("post") + print_exit_message(status, needs_update) + + +if __name__ == "__main__": + try: + main() + except GitReviewException as e: + print(e) + sys.exit(e.EXIT_CODE) -- 2.43.0