From 8610e75a5b4369f3797668abd6cbdcfe360c97d7 Mon Sep 17 00:00:00 2001 From: Ion Luca Date: Fri, 1 Aug 2025 15:12:25 +0200 Subject: [PATCH 1/8] Feature - Display how many commits are behind --- git-mr | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/git-mr b/git-mr index a4d3834..3415946 100755 --- a/git-mr +++ b/git-mr @@ -1203,6 +1203,9 @@ mr_print_status() { [[ -n $merge_request ]] || exit_error "$ERR_MR" "merge_request not provided" + # Updates remote references + $(git fetch origin 2>/dev/null) + local mr_url \ title \ labels \ @@ -1212,7 +1215,8 @@ mr_print_status() { merge_status \ pipeline_status \ pipeline_url \ - current_target + current_target \ + source_branch eval "$(echo "$merge_request" | jq -r ' "mr_url=" + (.web_url | @sh) + ";\n" + @@ -1226,7 +1230,8 @@ mr_print_status() { else .merge_status end | @sh) + ";\n" + "pipeline_status=" + (.head_pipeline.status | @sh) + ";\n" + "pipeline_url=" + (.head_pipeline.web_url | @sh) + ";\n" + - "current_target=" + (.target_branch | @sh) + ";\n" + "current_target=" + (.target_branch | @sh) + ";\n" + + "source_branch=" + (.source_branch | @sh) + ";\n" ')" # Labels @@ -1349,6 +1354,9 @@ mr_print_status() { fi fi + # Commits behind + local commits_behind=$(git rev-list --count "${source_branch}..origin/${current_target}" 2>/dev/null || echo "0") + # Display echo @@ -1382,7 +1390,13 @@ mr_print_status() { if [[ $thread_count -gt 0 ]]; then echo -n " Threads: ${threads_display} " else - echo -n " " + echo -n " " + fi + # Commits behind + if [[ $commits_behind -gt 0 ]]; then + echo -en " Behind: $(colorize "$commits_behind" "orange" "bold") commits " + else + echo -n " " fi # Pipeline if [[ -n $ci_str ]]; then From ef01ba09885ea9f8c6a97b129dfe1d1a00455367 Mon Sep 17 00:00:00 2001 From: Ion Luca Date: Thu, 14 Aug 2025 17:18:06 +0200 Subject: [PATCH 2/8] Feedbacks --- git-mr | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/git-mr b/git-mr index 3415946..6a30129 100755 --- a/git-mr +++ b/git-mr @@ -1162,6 +1162,9 @@ mr_status_block() { local mr_url=$6 local mr_title=$7 + local current_target + local source_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" +') @@ -1183,7 +1186,14 @@ mr_status_block() { [[ -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" + ')" + + local mr_commits_behind=$(git rev-list --count "${source_branch}..origin/${current_target}" 2>/dev/null || echo "0") + + mr_print_status "$merge_request" "$mr_approvals" "$mr_threads" "$mr_commits_behind" } mr_print_title() { @@ -1200,11 +1210,16 @@ mr_print_status() { local merge_request=$1 local approvals=$2 local threads=$3 + local commits_behind=$4 [[ -n $merge_request ]] || exit_error "$ERR_MR" "merge_request not provided" + local remote; remote=$(gitlab_remote || git_remote) + # Updates remote references - $(git fetch origin 2>/dev/null) + if [[ "$GIT_MR_AUTOFETCH" == "true" ]]; then + git fetch "$remote" 2>/dev/null + fi local mr_url \ title \ @@ -1215,8 +1230,7 @@ mr_print_status() { merge_status \ pipeline_status \ pipeline_url \ - current_target \ - source_branch + current_target eval "$(echo "$merge_request" | jq -r ' "mr_url=" + (.web_url | @sh) + ";\n" + @@ -1230,8 +1244,7 @@ mr_print_status() { else .merge_status end | @sh) + ";\n" + "pipeline_status=" + (.head_pipeline.status | @sh) + ";\n" + "pipeline_url=" + (.head_pipeline.web_url | @sh) + ";\n" + - "current_target=" + (.target_branch | @sh) + ";\n" + - "source_branch=" + (.source_branch | @sh) + ";\n" + "current_target=" + (.target_branch | @sh) + ";\n" ')" # Labels @@ -1354,9 +1367,6 @@ mr_print_status() { fi fi - # Commits behind - local commits_behind=$(git rev-list --count "${source_branch}..origin/${current_target}" 2>/dev/null || echo "0") - # Display echo @@ -3135,6 +3145,7 @@ GIT_MR_MENU_STATUS_SHOW=both #GIT_MR_MENU_STATUS_SHOW=branch GIT_MR_MENU_STATUS_TITLE_BRANCH_SEPARATOR=" " #GIT_MR_MENU_STATUS_TITLE_BRANCH_SEPARATOR="\n " +GIT_MR_AUTOFETCH=${GIT_MR_AUTOFETCH:-$(git config --get mr.git-mr-autofetch || false)} ################################################################################ # Run From 2bc70aad6f604ccdb96f69078f8b968011493cfb Mon Sep 17 00:00:00 2001 From: Djuuu Date: Fri, 22 Aug 2025 18:25:17 +0200 Subject: [PATCH 3/8] Extract git_autofetch() - add configurable auto-fetch interval (avoids fetching literally every time) - fetch before checking branch lag (move to mr_status_block along with git rev-list check) - fix shellcheck warning - use actual gitlab remote in git rev-list check - add auto-fetch config documentation --- README.md | 10 ++++++++++ git-mr | 39 ++++++++++++++++++++++++++++++--------- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 239e571..f5f226a 100644 --- a/README.md +++ b/README.md @@ -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:** @@ -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: diff --git a/git-mr b/git-mr index 6a30129..f7125ea 100755 --- a/git-mr +++ b/git-mr @@ -106,6 +106,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")}} @@ -1181,6 +1197,10 @@ mr_status_block() { mr_print_title "$mr_title" "$mr_url" + # Update remote references + local remote; remote=$(gitlab_remote || git_remote) + git_autofetch "${remote}" + # Read merge request, approvals & threads [[ -n $merge_request ]] || gitlab_read_mr merge_request [[ -n $mr_approvals ]] || gitlab_read_approvals mr_approvals @@ -1191,7 +1211,9 @@ mr_status_block() { "current_target=" + (.target_branch | @sh) + ";\n" ')" - local mr_commits_behind=$(git rev-list --count "${source_branch}..origin/${current_target}" 2>/dev/null || echo "0") + # Check branch lag + local mr_commits_behind + mr_commits_behind=$(git rev-list --count "${source_branch}..${remote}/${current_target}" 2>/dev/null || echo "0") mr_print_status "$merge_request" "$mr_approvals" "$mr_threads" "$mr_commits_behind" } @@ -1214,13 +1236,6 @@ mr_print_status() { [[ -n $merge_request ]] || exit_error "$ERR_MR" "merge_request not provided" - local remote; remote=$(gitlab_remote || git_remote) - - # Updates remote references - if [[ "$GIT_MR_AUTOFETCH" == "true" ]]; then - git fetch "$remote" 2>/dev/null - fi - local mr_url \ title \ labels \ @@ -3037,6 +3052,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 @@ -3133,19 +3151,22 @@ 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 #GIT_MR_MENU_STATUS_SHOW=branch GIT_MR_MENU_STATUS_TITLE_BRANCH_SEPARATOR=" " #GIT_MR_MENU_STATUS_TITLE_BRANCH_SEPARATOR="\n " -GIT_MR_AUTOFETCH=${GIT_MR_AUTOFETCH:-$(git config --get mr.git-mr-autofetch || false)} ################################################################################ # Run From f91ab08e44a08929f9bc6c43423053c7fb0559a5 Mon Sep 17 00:00:00 2001 From: Djuuu Date: Fri, 22 Aug 2025 18:26:05 +0200 Subject: [PATCH 4/8] Test git_autofetch() --- test/.gitconfig | 3 +++ test/git-mr.bats | 31 ++++++++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/test/.gitconfig b/test/.gitconfig index b6eb775..3eb0c86 100644 --- a/test/.gitconfig +++ b/test/.gitconfig @@ -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 diff --git a/test/git-mr.bats b/test/git-mr.bats index 78cd7f3..de91f43 100644 --- a/test/git-mr.bats +++ b/test/git-mr.bats @@ -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 @@ -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") From f4e338ea34848f4d897d96edfa051480ea19995f Mon Sep 17 00:00:00 2001 From: Djuuu Date: Fri, 22 Aug 2025 19:09:29 +0200 Subject: [PATCH 5/8] Move "behind" indicator next to target branch --- git-mr | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/git-mr b/git-mr index f7125ea..f291f86 100755 --- a/git-mr +++ b/git-mr @@ -1351,12 +1351,16 @@ 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 ]]; then + target_display="${target_display} $(colorize "↓$commits_behind" "red" "bold")" + (( 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= @@ -1415,13 +1419,7 @@ mr_print_status() { if [[ $thread_count -gt 0 ]]; then echo -n " Threads: ${threads_display} " else - echo -n " " - fi - # Commits behind - if [[ $commits_behind -gt 0 ]]; then - echo -en " Behind: $(colorize "$commits_behind" "orange" "bold") commits " - else - echo -n " " + echo -n " " fi # Pipeline if [[ -n $ci_str ]]; then From dda1ddf1ca4b45e329ad6159c77d7ae135964540 Mon Sep 17 00:00:00 2001 From: Djuuu Date: Fri, 22 Aug 2025 21:15:55 +0200 Subject: [PATCH 6/8] Fix 1st status line spacing --- git-mr | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/git-mr b/git-mr index f291f86..9def13d 100755 --- a/git-mr +++ b/git-mr @@ -1374,15 +1374,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 @@ -1392,14 +1395,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" From a4610149f6cca9aabe1abd54b25d5be71ba17a11 Mon Sep 17 00:00:00 2001 From: Djuuu Date: Tue, 26 Aug 2025 17:25:50 +0200 Subject: [PATCH 7/8] Also show commits behind main branch if target differs --- git-mr | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/git-mr b/git-mr index 9def13d..4c0e625 100755 --- a/git-mr +++ b/git-mr @@ -1180,6 +1180,7 @@ mr_status_block() { local current_target local source_branch + local default_branch local parse=() [[ -z $merge_request && -z $mr_iid ]] && parse+=('"mr_iid=" + (.iid | @sh) + ";\n" +') @@ -1201,6 +1202,8 @@ mr_status_block() { 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 @@ -1212,10 +1215,14 @@ mr_status_block() { ')" # Check branch lag - local mr_commits_behind + 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") - mr_print_status "$merge_request" "$mr_approvals" "$mr_threads" "$mr_commits_behind" + 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() { @@ -1233,6 +1240,7 @@ mr_print_status() { 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" @@ -1355,10 +1363,20 @@ mr_print_status() { local target_display target_display="${target_display}$(colorize "(\U000021A3 " "gray")" target_display="${target_display}$(colorize "$current_target" "lightpurple")" - if [[ $commits_behind -gt 0 ]]; then - target_display="${target_display} $(colorize "↓$commits_behind" "red" "bold")" + + 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")" From e0f6856569484299f21a7913927e85a3f6e4789a Mon Sep 17 00:00:00 2001 From: Djuuu Date: Wed, 27 Aug 2025 10:47:15 +0200 Subject: [PATCH 8/8] Check & show branch lag on git mr update --- git-mr | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/git-mr b/git-mr index 4c0e625..928bb5d 100755 --- a/git-mr +++ b/git-mr @@ -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" || @@ -2379,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") @@ -2673,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() {