Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,11 @@ git config --global mr.jira-ok-id "xx"

# Network timeout (in seconds, defaults to 10)
# git config --global mr.git-mr-timeout 10

# Auto-fetch periodically (defaults to false)
# git config --global mr.git-mr-autofetch true
# Auto-fetch interval (in minutes)
# git config --global mr.git-mr-autofetch-interval 5
```

**Tip:**
Expand Down Expand Up @@ -279,6 +284,11 @@ export JIRA_OK_ID="xx"

# Network timeout (in seconds, defaults to 10)
#export GIT_MR_TIMEOUT=10

# Auto-fetch periodically (defaults to false)
#GIT_MR_AUTOFETCH=true
# Auto-fetch interval (in minutes)
#GIT_MR_AUTOFETCH_INTERVAL=5
```

Environment-only configuration:
Expand Down
111 changes: 96 additions & 15 deletions git-mr
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,12 @@ git_remote_branch_exists() {
git_check_branches() {
local source_branch="$1"
local target_branch="$2"
local default_branch="$3"

[[ -n $source_branch ]] ||
exit_error "$ERR_GIT" "Not on any branch"

[[ $source_branch != "$(git_default_branch)" ]] ||
[[ $source_branch != "${default_branch:-$(git_default_branch)}" ]] ||
exit_error "$ERR_GIT" "On default branch"

git_branch_exists "$source_branch" ||
Expand All @@ -106,6 +107,22 @@ git_check_branches() {
return 0
}

git_autofetch() {
[[ "$GIT_MR_AUTOFETCH" == "true" ]] ||
return 1 # autofetch is disabled

local git_dir; git_dir=$(git rev-parse --git-dir)

[[ -f "${git_dir}/FETCH_HEAD" &&
-z "$(find "${git_dir}/FETCH_HEAD" -mmin +"${GIT_MR_AUTOFETCH_INTERVAL}" 2>/dev/null)" ]] &&
return 1 # last fetch is recent enough

local remote; remote=${1:-$(gitlab_remote || git_remote)}

echo_debug "Fetching remote: ${remote}"
git fetch --quiet "${remote}" 2>/dev/null
}

git_commits() {
local source_branch=${1:-$(git_current_branch)}
local target_branch=${2:-${GIT_MR_TARGET:-$(git_base_branch "$source_branch")}}
Expand Down Expand Up @@ -1162,6 +1179,10 @@ mr_status_block() {
local mr_url=$6
local mr_title=$7

local current_target
local source_branch
local default_branch

local parse=()
[[ -z $merge_request && -z $mr_iid ]] && parse+=('"mr_iid=" + (.iid | @sh) + ";\n" +')
[[ -z $mr_url ]] && parse+=('"mr_url=" + (.web_url | @sh) + ";\n" +')
Expand All @@ -1178,12 +1199,31 @@ mr_status_block() {

mr_print_title "$mr_title" "$mr_url"

# Update remote references
local remote; remote=$(gitlab_remote || git_remote)
git_autofetch "${remote}"

default_branch=$(git_default_branch)

# Read merge request, approvals & threads
[[ -n $merge_request ]] || gitlab_read_mr merge_request
[[ -n $mr_approvals ]] || gitlab_read_approvals mr_approvals
[[ -n $mr_threads ]] || gitlab_read_threads mr_threads

mr_print_status "$merge_request" "$mr_approvals" "$mr_threads"
eval "$(echo "$merge_request" | jq -r '
"source_branch=" + (.source_branch | @sh) + ";\n" +
"current_target=" + (.target_branch | @sh) + ";\n"
')"

# Check branch lag
local mr_commits_behind mr_commits_behind_main
mr_commits_behind=$(git rev-list --count "${source_branch}..${remote}/${current_target}" 2>/dev/null || echo "0")

if [[ $current_target != "$default_branch" ]]; then
mr_commits_behind_main=$(git rev-list --count "${source_branch}..${remote}/${default_branch}" 2>/dev/null || echo "0")
fi

mr_print_status "$merge_request" "$mr_approvals" "$mr_threads" "$mr_commits_behind" "$mr_commits_behind_main"
}

mr_print_title() {
Expand All @@ -1200,6 +1240,8 @@ mr_print_status() {
local merge_request=$1
local approvals=$2
local threads=$3
local commits_behind=$4
local commits_behind_main=$5

[[ -n $merge_request ]] || exit_error "$ERR_MR" "merge_request not provided"

Expand Down Expand Up @@ -1318,12 +1360,26 @@ mr_print_status() {
ci_str="CI: $pipeline_icon"

# Merge target
local target_str_len=$((${#current_target} + 4))
local target_display
target_display="${target_display}$(colorize "(\U000021A3 " "gray")"
target_display="${target_display}$(colorize "$current_target" "lightpurple")"

if [[ $commits_behind -gt 0 || $commits_behind_main -gt 0 ]]; then
if [[ $commits_behind -gt 0 ]]
then target_display="${target_display} $(colorize "↓$commits_behind" "red" "bold")"
else target_display="${target_display} $(colorize "↓0" "green")"
fi
(( target_str_len += 4 )) # space, arrow, 2 digits
fi

if [[ $commits_behind_main -gt 0 ]]; then
target_display="${target_display} $(colorize "⇣$commits_behind_main" "red")"
(( target_str_len += 4 )) # space, arrow, 2 digits
fi

target_display="${target_display}$(colorize ")" "gray")"

local target_str_len=$((${#current_target} + 4))

# Draft status
local draft_str=
Expand All @@ -1337,15 +1393,18 @@ mr_print_status() {
# Spacers for draft & target branch indicators
local display_width=76 # not counting 3 leading spaces
local spacer_chars=$((display_width - labels_str_len - target_str_len - draft_str_len))
local spacer_chars_l=0
local spacer_chars_r=0
local draft_spacer_l=0
local draft_spacer_r=0

if [[ $spacer_chars -gt 0 ]]; then
spacer_chars_l=$((43 - labels_str_len)) # 42: roughly at the same level as CI indicator
spacer_chars_r=$((spacer_chars - spacer_chars_l))
if [[ $spacer_chars_r -lt 0 ]]; then
spacer_chars_l=$((spacer_chars_l + spacer_chars_r))
spacer_chars_r=0
draft_spacer_l=$((43 - labels_str_len)) # 43: roughly at the same level as CI indicator
draft_spacer_r=$((spacer_chars - draft_spacer_l))
if [[ $draft_spacer_r -lt 0 ]]; then
draft_spacer_l=$((draft_spacer_l + draft_spacer_r))
draft_spacer_r=0
elif [[ $draft_spacer_l -lt 0 ]]; then
draft_spacer_r=$((draft_spacer_l + draft_spacer_r))
draft_spacer_l=0
fi
fi

Expand All @@ -1355,14 +1414,14 @@ mr_print_status() {
# 1st row ------------------------------------------------------------------
# Labels
echo -en " \U0001F3F7 ${labels_display}"
echo_spacer "$spacer_chars_l"
echo_spacer "$draft_spacer_l"
# Draft status
if [[ -n $draft_str ]]; then
echo -en " $(colorize "$draft_str" "orange") "
else
echo_spacer $((2 - spacer_chars_l - spacer_chars_r))
echo_spacer $((2 - draft_spacer_l - draft_spacer_r))
fi
echo_spacer "$spacer_chars_r"
echo_spacer "$draft_spacer_r"
# Target
echo "$target_display"

Expand Down Expand Up @@ -2321,7 +2380,9 @@ mr_update() {

local source_branch=${1:-$(git_current_branch)}
local target_branch=${GIT_MR_TARGET:-$(git_base_branch "$source_branch")}
git_check_branches "$source_branch" "$target_branch"
local default_branch; default_branch=$(git_default_branch)

git_check_branches "$source_branch" "$target_branch" "$default_branch"

# Search existing merge request
local mr_summary; mr_summary=$(gitlab_merge_request_summary "$source_branch")
Expand Down Expand Up @@ -2615,9 +2676,22 @@ mr_update() {
gitlab_read_approvals mr_approvals
gitlab_read_threads mr_threads

# Re-read current_target from MR (might have changed)
eval "$(echo "$merge_request" | jq -r '
"current_target=" + (.target_branch | @sh) + ";\n"
')"

# Check branch lag
local mr_commits_behind mr_commits_behind_main
mr_commits_behind=$(git rev-list --count "${source_branch}..${remote}/${current_target}" 2>/dev/null || echo "0")

if [[ $current_target != "$default_branch" ]]; then
mr_commits_behind_main=$(git rev-list --count "${source_branch}..${remote}/${default_branch}" 2>/dev/null || echo "0")
fi

# --------------------------------------------------------------------------------
mr_print_title "$mr_title" "$mr_url"
mr_print_status "$merge_request" "$mr_approvals" "$mr_threads"
mr_print_status "$merge_request" "$mr_approvals" "$mr_threads" "$mr_commits_behind" "$mr_commits_behind_main"
}

mr_merge() {
Expand Down Expand Up @@ -3013,6 +3087,9 @@ EOF
| mr.git-mr-required-upvotes | GIT_MR_REQUIRED_UPVOTES |
| | |
| mr.git-mr-timeout | GIT_MR_TIMEOUT |
| | |
| mr.git-mr-autofetch | GIT_MR_AUTOFETCH |
| mr.git-mr-autofetch-interval | GIT_MR_AUTOFETCH_INTERVAL |
+---------------------------------------------+---------------------------------------+

To create a Jira API Token, go to: https://id.atlassian.com/manage-profile/security/api-tokens
Expand Down Expand Up @@ -3109,12 +3186,16 @@ GIT_MR_EXTENDED=${GIT_MR_EXTENDED-$(git config --get mr.git-mr-extended || true)
GIT_MR_REQUIRED_UPVOTES=${GIT_MR_REQUIRED_UPVOTES:-$(git config --get mr.git-mr-required-upvotes || true)}
GITLAB_REMOVE_SOURCE_BRANCH_ON_MERGE=${GITLAB_REMOVE_SOURCE_BRANCH_ON_MERGE:-$(git config --get mr.gitlab-remove-source-branch-on-merge || true)}
GIT_MR_TIMEOUT=${GIT_MR_TIMEOUT:-$(git config --get mr.git-mr-timeout || true)}
GIT_MR_AUTOFETCH=${GIT_MR_AUTOFETCH:-$(git config --get mr.git-mr-autofetch || true)}
GIT_MR_AUTOFETCH_INTERVAL=${GIT_MR_AUTOFETCH_INTERVAL:-$(git config --get mr.git-mr-autofetch-interval || true)}

# Defaults
GITLAB_PROJECTS_LIMIT_MEMBER=${GITLAB_PROJECTS_LIMIT_MEMBER:-1}
GIT_MR_REQUIRED_UPVOTES=${GIT_MR_REQUIRED_UPVOTES:-2}
GITLAB_REMOVE_SOURCE_BRANCH_ON_MERGE=${GITLAB_REMOVE_SOURCE_BRANCH_ON_MERGE:-1}
GIT_MR_TIMEOUT=${GIT_MR_TIMEOUT:-10}
GIT_MR_AUTOFETCH=${GIT_MR_AUTOFETCH:-false}
GIT_MR_AUTOFETCH_INTERVAL=${GIT_MR_AUTOFETCH_INTERVAL:-5}

GIT_MR_MENU_STATUS_SHOW=both
#GIT_MR_MENU_STATUS_SHOW=title
Expand Down
3 changes: 3 additions & 0 deletions test/.gitconfig
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,6 @@
git-mr-required-upvotes = 2
gitlab-remove-source-branch-on-merge = 1
git-mr-timeout = 1

git-mr-autofetch = false
git-mr-autofetch-interval = 1
31 changes: 30 additions & 1 deletion test/git-mr.bats
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ setup_file() {
GITLAB_IP_LABELS GITLAB_CR_LABELS GITLAB_QA_LABELS GITLAB_OK_LABELS \
JIRA_IP_ID JIRA_CR_ID JIRA_QA_ID JIRA_OK_ID \
GITLAB_PROJECTS_LIMIT_MEMBER \
GIT_MR_EXTENDED GIT_MR_REQUIRED_UPVOTES GIT_MR_TIMEOUT
GIT_MR_EXTENDED GIT_MR_REQUIRED_UPVOTES GIT_MR_TIMEOUT \
GIT_MR_AUTOFETCH GIT_MR_AUTOFETCH_INTERVAL

export GIT_MR_NO_COLORS=1
export GIT_MR_NO_TERMINAL_LINK=1
Expand Down Expand Up @@ -283,6 +284,34 @@ sha_link() {
assert_output "Branch 'wrong' does not exist"
}

@test "Auto-fetches remote" {
GIT_MR_VERBOSE=1

# Not fetched yet
[ ! -f .git/FETCH_HEAD ]

# No auto-fetch if not configured
GIT_MR_AUTOFETCH=false
run git_autofetch
assert_output ""

# Auto-fetch if configured
GIT_MR_AUTOFETCH=true
run git_autofetch
assert_output "Fetching remote: gitlab"

# Remote was fetched
[ -f .git/FETCH_HEAD ]

# No auto-fetch if fetch is recent enough
run git_autofetch
assert_output ""

# reset
GIT_MR_AUTOFETCH=false
GIT_MR_VERBOSE=0
}

@test "Lists current branch commits" {
testSha1=$(short_sha "Feature test - 1")
testSha2=$(short_sha "Feature test - 2")
Expand Down