Skip to content

Commit

Permalink
Merge pull request #16 from Djuuu/fix/14-link-to-merge-request-diff-b…
Browse files Browse the repository at this point in the history
…y-commit

Use links to merge request diff view
  • Loading branch information
Djuuu authored Feb 26, 2024
2 parents ad909d8 + 1d98624 commit ab7136a
Show file tree
Hide file tree
Showing 7 changed files with 488 additions and 131 deletions.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,24 @@ You can also update the source branch if it is different from the current one.
![git mr update](doc/git-mr-update.png)
#### Note on commit links
The initial merge request description lists commit SHA-1 and and message in a simple format.
When Gitlab recognizes a partial Git SHA-1 in a description, it will automatically create a link to the commit,
but this link has no reference to the current merge request:
> `https://myapp.gitlab.com/my-project/-/commit/0a1b2c3d4e5f`
If a comment is created from this commit page, it might appear in the merge request at first,
but **it will disappear as soon as the branch is rebased**.
Once the merge request exists, `git mr update` will convert SHA-1 references to **links to the commit in the merge request diff view**:
> `https://myapp.gitlab.com/my-project/-/merge_requests/123/diffs?commit_id=0a1b2c3d4e5f`
This view allows navigating through the merge request commits, and comments left on this page will remain attached to the merge request.
![git mr update links](doc/git-mr-update-links.png)
----------------------------------------------------------------
### `git mr menu`
Expand Down
41 changes: 41 additions & 0 deletions doc/generate-sample-output.sh
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,45 @@ $(mr_print_status "$mr" "$threads")
EOF
}

sample_mr_update_links() {
fake_prompt "git mr update"

local mr='{
"title": "Draft: '"$mr_title"'", "web_url":"'"$mr_url"'",
"labels":["Testing","My Team"], "target_branch": "main",
"upvotes": 2, "downvotes": 0, "merge_status": "can_be_merged",
"head_pipeline": {"status":"running", "web_url":"https://myapp.gitlab.com/my/project/pipelines/6"}
}'

local threads='1 unresolved:false note_id:2
2 unresolved:true note_id:2'

cat << EOF
$(mr_print_title "$mr_title" "$mr_url")
$(markdown_title "$ticket_link")
Vivamus venenatis tortor et neque sollicitudin, eget suscipit est malesuada.
Suspendisse nec odio id arcu sagittis pulvinar ut nec lacus.
Sed non nulla ac metus congue consectetur et vel magna.
## Commits
* **$(c_same "78330c9")🔗✔ In vulputate quam ac ultrices volutpat**
* **$(c_same "0010a6a")🔗✔ Curabitur vel purus sed tortor finibus posuere**
* **$(c_same "aac348f")🔗✔ Aenean sed sem hendrerit ex egestas tincidunt**
--------------------------------------------------------------------------------
upgraded links: $(c_same "3") 🔗
$(c_question "Do you want to update the merge request description?") [y/N]
EOF
}

sample_mr_merge() {
fake_prompt "git mr merge"

Expand Down Expand Up @@ -375,6 +414,7 @@ if [[ "$#" -gt 0 ]]; then
;;
update)
sample_mr_update
sample_mr_update_links
;;
menu)
sample_mr_menu
Expand All @@ -396,6 +436,7 @@ else
sample_mr_status

sample_mr_update
sample_mr_update_links

sample_mr_menu
sample_mr_menu_status
Expand Down
Binary file added doc/git-mr-update-links.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
177 changes: 127 additions & 50 deletions git-mr
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,13 @@ git_commits() {
git log --oneline --reverse --no-decorate "${target_branch}..${source_branch}"
}

git_commits_hybrid() {
local source_branch=${1:-$(git_current_branch)}
local target_branch=${2:-${GIT_MR_TARGET:-$(git_base_branch "$source_branch")}}

git log --reverse --format="%H %h %s" "${target_branch}".."${source_branch}"
}

git_commits_extended() {
local source_branch=${1:-$(git_current_branch)}
local target_branch=${2:-${GIT_MR_TARGET:-$(git_base_branch "$source_branch")}}
Expand All @@ -129,7 +136,7 @@ git_commits_extended() {
sed -r "s/${end}$//g" # remove trailing body end for bodies without trailing blank line
}

git_commit_extended() {
git_commit_extended_console_display() {
local revision=${1:-HEAD}

local prefix="* ${MD_BOLD}"
Expand Down Expand Up @@ -371,7 +378,7 @@ jq_build() {

# escape regex special characters for literal usage
regex_escape() {
echo "${1}" | sed -e 's/[]\/$*.^[]/\\&/g'
echo "${1}" | sed -e 's/[]\/$*.^?[]/\\&/g'
}

open_in_browser() {
Expand Down Expand Up @@ -1289,17 +1296,24 @@ mr_print_status() {
mr_description_first_commit_line() {
local mr_description=$1

local first_commit_line
first_commit_line=$(echo "$mr_description" | grep -Pn '^[^0-9a-fA-F]*[0-9a-fA-F]{7,}\s' -m 1 | cut -d: -f1)
local first_commit_line; first_commit_line=$(
echo "$mr_description" |
grep -Pn "$_git_mr_commit_regex" -m 1 |
cut -d: -f1
)

echo "$first_commit_line"
}

mr_description_last_commit_line() {
local mr_description=$1

local last_commit_line
last_commit_line=$(echo "$mr_description" | grep -Pn '^[^0-9a-fA-F]*[0-9a-fA-F]{7,}\s' | tail -n1 | cut -d: -f1)
local last_commit_line; last_commit_line=$(
echo "$mr_description" |
grep -Pn "$_git_mr_commit_regex" |
tail -n1 |
cut -d: -f1
)

local description_after_last_commit
description_after_last_commit=$(echo "$mr_description" | tail -n "+$((last_commit_line + 1))")
Expand Down Expand Up @@ -1328,20 +1342,10 @@ mr_description_insert_new_commits() {

[[ -n $last_commit_line ]] || last_commit_line=$(mr_description_last_commit_line "$mr_description")

local description_length; description_length=$(echo "$mr_description" | wc -l)

# Iterate over description lines
local i=1 mr_description_line trailing_br
local i=1 mr_description_line
while IFS=$'\n' read -r mr_description_line; do

# Add trailing markdown newline (that would have been trimmes by Gitlab) to list item on last line.
# (As other commit lines, to prepare for an extended description going on the next line.)
[[ $i -eq $description_length && $i -eq $last_commit_line ]] &&
grep -q '^* ' <<< "$mr_description_line" &&
trailing_br="$MD_BR"

echo "${mr_description_line}${trailing_br}"

echo "${mr_description_line}"
# Insert new commits
if [[ $i -eq $last_commit_line ]]; then
mr_description_print_new_commits "$new_commits" "$for_terminal"
Expand Down Expand Up @@ -2057,17 +2061,27 @@ mr_update() {

# Init commit lists

local commit_messages; commit_messages=$(git_commits "$source_branch" "$target_branch")
local commit_reference_combo; commit_reference_combo=$(git_commits_hybrid "$source_branch" "$target_branch")

local current_commits; current_commits=$(echo "$commit_messages" | cut -d ' ' -f1)
local old_commits; old_commits=$(echo "$mr_description" |
grep -Po '^[^0-9a-fA-F]*[0-9a-fA-F]{7,}\s' |
sed -r 's/^[^0-9a-fA-F]*([0-9a-fA-F]{7,})\s/\1/g')
local current_commits_long; current_commits_long=$(echo "$commit_reference_combo" | cut -d ' ' -f1)
local current_commits_short; current_commits_short=$(echo "$commit_reference_combo" | cut -d ' ' -f2)
local commit_messages; commit_messages=$(echo "$commit_reference_combo" | cut -d ' ' -f3-)

local old_commits_array current_commits_array oIFS
local old_commit_lines; old_commit_lines=$(echo "$mr_description" | grep -P "${_git_mr_commit_regex}")

local old_commits; old_commits=$(echo "$old_commit_lines" |
sed -r -e "s/^${_git_mr_commit_prefix}(${_git_mr_commit_ref})\s.*/\1/g" \
-e "s/^${_git_mr_commit_prefix}\[(${_git_mr_commit_ref})\]\(http[^)]+\)\s.*/\1/g")

local old_commits_array oIFS
oIFS=$IFS; IFS=$'\n'
# shellcheck disable=SC2206
current_commits_array=($current_commits)
current_commits_long_array=($current_commits_long)
# shellcheck disable=SC2206
current_commits_short_array=($current_commits_short)
# shellcheck disable=SC2206
current_commits_messages_array=($commit_messages)

# shellcheck disable=SC2206
old_commits_array=($old_commits)
IFS=$oIFS
Expand All @@ -2090,7 +2104,7 @@ mr_update() {
fi

local updated_commit_count=0
local new_commit=
local upgraded_link_count=0
local new_commit_messages_display=()
local new_commit_messages_content=()

Expand All @@ -2103,43 +2117,94 @@ mr_update() {
local wrongColor="lightred"

# Iterate over commit lists, compare sha-1 and update description
for i in ${!current_commits_array[*]}; do
for i in ${!current_commits_long_array[*]}; do

local oldSha=${old_commits_array[$i]}

local currSha=${current_commits_short_array[$i]}
local currShaaaa=${current_commits_long_array[$i]}
local currMsg=${current_commits_messages_array[$i]}

local oldCommitUrl="$mr_url/diffs?commit_id=$oldSha"
local currCommitUrl="$mr_url/diffs?commit_id=$currShaaaa"

local msgSuffix matchOldCommitUrl matchOldLogOrLink matchOldLog matchLink

msgSuffix="(.*)($(regex_escape "$MD_BOLD"))($(regex_escape "$MD_BR"))?"
matchOldCommitUrl="$(regex_escape "$oldCommitUrl")[0-9a-fA-F]*"

matchOldLogOrLink="^(${_git_mr_commit_prefix})(${oldSha}|\[${oldSha}\]\(${matchOldCommitUrl}\)) ${msgSuffix}"
matchOldLog="^(${_git_mr_commit_prefix})(${oldSha}) ${msgSuffix}"
matchLink="^(${_git_mr_commit_prefix})(\[${oldSha}\]\(${matchOldCommitUrl}\)) ${msgSuffix}"

local consoleShaColor
if [[ -n $oldSha ]]; then if [[ $oldSha == "$currSha" ]]; then
consoleShaColor="$sameColor"; else
consoleShaColor="$updatedColor"; fi; else
consoleShaColor="$newColor"; fi

local forConsole_converted forConsole_link forDescription forDescriptionNew
forConsole_converted="\1$(colorize "$currSha" "$consoleShaColor")🔗✔ \3\4${MD_BR}"
forConsole_link="\1$(colorize "$currSha" "$consoleShaColor")🔗 \3\4${MD_BR}"
forDescription="\1[$currSha]($(regex_escape "$currCommitUrl")) \3\4${MD_BR}"
forDescriptionNew="\1[$currSha]($(regex_escape "$currCommitUrl")) \3"

if [[ -n $oldSha ]]; then
# Existing commit
# echo "🚧 Existing"
new_description_display_before="$new_description_display"

# shellcheck disable=SC2001
new_description_display=$(echo "$new_description_display" | sed -r -e "s/${matchOldLog}/${forConsole_converted}/g")
if [[ "$new_description_display" != "$new_description_display_before" ]]; then
upgraded_link_count=$((upgraded_link_count + 1))
else
# shellcheck disable=SC2001
new_description_display=$(echo "$new_description_display" | sed -r -e "s/${matchLink}/${forConsole_link}/g")
fi

local curr=${current_commits_array[$i]}
local old=${old_commits_array[$i]}
# shellcheck disable=SC2001
new_description_content=$(echo "$new_description_content" | sed -r -e "s/${matchOldLogOrLink}/${forDescription}/g")

if [[ -n $old ]]; then
if [[ $old == "$curr" ]]; then
# same sha-1 - only decorate
new_description_display=${new_description_display//"$old"/"$(colorize "$curr" "$sameColor")"}
new_description_content=${new_description_content//"$old"/"$curr"}
else
# different sha-1 - replace & decorate
new_description_display=${new_description_display//"$old"/"$(colorize "$curr" "$updatedColor")"}
new_description_content=${new_description_content//"$old"/"$curr"}
if [[ $oldSha != "$currSha" ]]; then
# Updated commit
updated_commit_count=$((updated_commit_count + 1))
fi
else
# new commit
[[ $GIT_MR_EXTENDED -eq 1 ]] &&
new_commit="$(git_commit_extended "$curr")" ||
new_commit="$(echo "$commit_messages" | grep "$curr")"
# New commit
local new_commit_line_display new_commit_line_content

if [[ $GIT_MR_EXTENDED -eq 1 ]]; then
# shellcheck disable=SC2001
new_commit_line_display="$(git_commit_extended_console_display "$currSha")"
# shellcheck disable=SC2001
new_commit_line_content="$(echo "$new_commit_line_display" | sed -r -e "s/^(\* \*\*)(${currSha}) (.*)/${forDescriptionNew}/")"
# shellcheck disable=SC2001
new_commit_line_display="$(echo "$new_commit_line_display" |
sed -r -e "s/^(\* \*\*)(${currSha}) (.*)$/\1$(colorize "$currSha" "$consoleShaColor")🔗 \3/"
)"
else
new_commit_line_display="$(colorize "$currSha" "$consoleShaColor")🔗 ${currMsg}"
new_commit_line_content="[$currSha]($currCommitUrl) ${currMsg}"
fi

new_commit_messages_display+=("${new_commit//"$curr"/$(colorize "$curr" "$newColor")}")
new_commit_messages_content+=("$new_commit")
new_commit_messages_display+=("$new_commit_line_display")
new_commit_messages_content+=("$new_commit_line_content")
fi
done

local old_commit_count=${#old_commits_array[@]}
local current_commit_count=${#current_commits_array[@]}
local current_commit_count=${#current_commits_short_array[@]}
local new_commit_count=${#new_commit_messages_content[@]}
local unknown_commit_count=$((old_commit_count - current_commit_count))

if [[ $unknown_commit_count -gt 0 ]]; then
for (( i = current_commit_count; i < old_commit_count; i++ )); do
old="${old_commits_array[$i]}"
new_description_display=${new_description_display//"$old"/"$(colorize "$old" "$wrongColor")"}
oldSha="${old_commits_array[$i]}"
new_description_display=${new_description_display//"$oldSha"/"$(colorize "$oldSha" "$wrongColor")"}
done
else
unknown_commit_count=0
fi

# implode arrays
Expand All @@ -2163,8 +2228,15 @@ mr_update() {
echo
echo "--------------------------------------------------------------------------------"
echo
echo " updated commits: $(colorize "$updated_commit_count" "$updatedColor")"
echo " new commits: $(colorize "$new_commit_count" "$newColor")"
if [[ $upgraded_link_count -gt 0 ]]; then
echo " upgraded links: $(colorize "$upgraded_link_count" "$sameColor") 🔗"
fi
if [[ $updated_commit_count -gt 0 ]]; then
echo " updated commits: $(colorize "$updated_commit_count" "$updatedColor")"
fi
if [[ $new_commit_count -gt 0 ]]; then
echo " new commits: $(colorize "$new_commit_count" "$newColor")"
fi
if [[ $unknown_commit_count -gt 0 ]]; then
echo
echo " unknown commits: $(colorize "$unknown_commit_count" "$wrongColor")"
Expand All @@ -2181,7 +2253,7 @@ mr_update() {
echo_error
fi

if [[ $((updated_commit_count + new_commit_count)) -gt 0 ]]; then
if [[ $((upgraded_link_count + updated_commit_count + new_commit_count)) -gt 0 ]]; then
update_prompt=1

local remote; remote=$(gitlab_remote)
Expand Down Expand Up @@ -2690,6 +2762,11 @@ GIT_MR_MENU_START=${GIT_MR_MENU_START:-"## Menu"}
GIT_MR_MENU_END=${GIT_MR_MENU_END:-"--------------------------------------------------------------------------------"}
GIT_MR_MENU_UPDATE_CONTEXT_LINES=${GIT_MR_MENU_UPDATE_CONTEXT_LINES:-15}

_git_mr_commit_prefix='[^0-9a-fA-F]*'
_git_mr_commit_ref='[0-9a-fA-F]{7,}'
_git_mr_commit_href="\[${_git_mr_commit_ref}\]\(http[^)]+\)"
_git_mr_commit_regex="^${_git_mr_commit_prefix}(${_git_mr_commit_ref}|${_git_mr_commit_href})\s"

# Custom file descriptors
git_mr_fd_mr=${GIT_MR_FD_MR=}
git_mr_fd_th=${GIT_MR_FD_TH=}
Expand Down
Loading

0 comments on commit ab7136a

Please sign in to comment.