From e8701195e66f2d27ffe17fb514eae8173795aaf7 Mon Sep 17 00:00:00 2001 From: Georgiy Bondarenko <69736697+nehilo@users.noreply.github.com> Date: Thu, 4 Mar 2021 22:54:23 +0500 Subject: Initial commit --- buildroot/share/git/README.md | 60 +++++++++ buildroot/share/git/firstpush | 28 ++++ buildroot/share/git/ghpc | 68 ++++++++++ buildroot/share/git/ghtp | 39 ++++++ buildroot/share/git/mfadd | 36 +++++ buildroot/share/git/mfclean | 32 +++++ buildroot/share/git/mfconfig | 183 +++++++++++++++++++++++++ buildroot/share/git/mfdoc | 38 ++++++ buildroot/share/git/mffp | 27 ++++ buildroot/share/git/mfhelp | 28 ++++ buildroot/share/git/mfinfo | 61 +++++++++ buildroot/share/git/mfinit | 17 +++ buildroot/share/git/mfnew | 34 +++++ buildroot/share/git/mfpr | 37 +++++ buildroot/share/git/mfpub | 138 +++++++++++++++++++ buildroot/share/git/mfqp | 30 +++++ buildroot/share/git/mfrb | 27 ++++ buildroot/share/git/mftest | 306 ++++++++++++++++++++++++++++++++++++++++++ buildroot/share/git/mfup | 48 +++++++ 19 files changed, 1237 insertions(+) create mode 100644 buildroot/share/git/README.md create mode 100644 buildroot/share/git/firstpush create mode 100644 buildroot/share/git/ghpc create mode 100644 buildroot/share/git/ghtp create mode 100644 buildroot/share/git/mfadd create mode 100644 buildroot/share/git/mfclean create mode 100644 buildroot/share/git/mfconfig create mode 100644 buildroot/share/git/mfdoc create mode 100644 buildroot/share/git/mffp create mode 100644 buildroot/share/git/mfhelp create mode 100644 buildroot/share/git/mfinfo create mode 100644 buildroot/share/git/mfinit create mode 100644 buildroot/share/git/mfnew create mode 100644 buildroot/share/git/mfpr create mode 100644 buildroot/share/git/mfpub create mode 100644 buildroot/share/git/mfqp create mode 100644 buildroot/share/git/mfrb create mode 100644 buildroot/share/git/mftest create mode 100644 buildroot/share/git/mfup (limited to 'buildroot/share/git') diff --git a/buildroot/share/git/README.md b/buildroot/share/git/README.md new file mode 100644 index 0000000..4dcd7c1 --- /dev/null +++ b/buildroot/share/git/README.md @@ -0,0 +1,60 @@ +## Marlin Github Helper Scripts + +### Introduction + +A Pull Request is often just the start of a longer process of patching and refining the code until it's ready to merge. In that process it's common to accumulate a lot of commits, some of which are non-functional. Before merging any PR, excess commits need to be "squashed" and sometimes rearranged or reworked to produce a well-packaged set of changes and keep the commit history relatively clean. + +In addition, while a PR is being worked on other commits may be merged, leading to conflicts that need resolution. For this reason, it's a best practice to periodically refresh the PR so the working copy closely reflects the final merge into upstream `MarlinFirmware`. + +#### Merge vs Rebase + +If you plan to create PRs and work on them after submission I recommend not using Github Desktop to sync and merge. Use the command line instead. Github Desktop provides a "merge" option, but I've found that "`git rebase`" is much cleaner and easier to manage. Merge applies new work _after_ your commits, which buries them deeper in the commit history and makes it hard to bring them together as a final packaged unit. Rebase helpfully moves your commits to the tip of the branch, ensuring that your commits are adapted to the current code. This makes it easier to keep revising the commits in-place. + +### The Scripts + +The following scripts can be used on any system with a GNU environment to speed up the process of working with Marlin branches and submitting changes to the project. + +#### Remotes + +File|Description +----|----------- +mfadd [user]|Add and Fetch Remote - Add and fetch another user's Marlin fork. Optionally, check out one of their branches. +mfinit|Init Working Copy - Create a remote named '`upstream`' (for use by the other scripts) pointing to the '`MarlinFirmware`' fork. This only needs to be used once. Newer versions of Github Desktop may create `upstream` on your behalf. + +#### Branches + +File|Description +----|----------- +mfnew [branch]|New Branch - Creates a new branch based on `upstream/[PR-target]`. All new work should start with this command. +mffp|Fast Push - Push the HEAD or a commit ID to `upstream` immediately. Requires privileged access to the MarlinFirmware repo. +firstpush|Push the current branch to 'origin' -your fork on Github- and set it to track '`origin`'. The branch needs to reside on Github before you can use it to make a PR. + +#### Making / Amending PRs + +File|Description +----|----------- +mfpr|Pull Request - Open the Compare / Pull Request page on Github for the current branch. +mfrb|Do a `git rebase` then `git rebase -i` of the current branch onto `upstream/[PR-target]`. Use this to edit your commits anytime. +mfqp|Quick Patch - Commit all current changes as "patch", then do `mfrb`, followed by `git push -f` if no conflicts need resolution. + +#### Documentation + +File|Description +----|----------- +mfdoc|Build the documentation with Jekyll and preview it locally. +mfpub|Build and publish the documentation to marlinfw.org. + +#### Utilities + +File|Description +----|----------- +ghtp -[h/s]|Set the protocol to use for all remotes. -h for HTTPS, -s for SSL. +ghpc [-f]|Push current branch to 'origin' or to the remote indicated by the error. +mfinfo|This utility script is used by the other scripts to get:
- The upstream project ('`MarlinFirmware`')
- the '`origin`' project (i.e., your Github username),
- the repository name ('`Marlin`'),
- the PR target branch ('`bugfix-1.1.x`'), and
- the current branch (or the first command-line argument).

By itself, `mfinfo` simply prints these values to the console. +mfclean     |Prune your merged and remotely-deleted branches. + +--- + +### Examples + +For a demonstration of these scripts see the video [Marlin Live - May 9 2019](https://youtu.be/rwT4G0uVTIY). There is also an old write-up at [#3193](https://github.com/MarlinFirmware/Marlin/issues/3193). diff --git a/buildroot/share/git/firstpush b/buildroot/share/git/firstpush new file mode 100644 index 0000000..9a68fc5 --- /dev/null +++ b/buildroot/share/git/firstpush @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# +# firstpush +# +# Push a branch to 'origin' and open the +# commit log to watch Travis CI progress. +# + +[[ $# == 0 ]] || { echo "usage: `basename $0`" 1>&2 ; exit 1; } + +MFINFO=$(mfinfo) || exit 1 +IFS=' ' read -a INFO <<< "$MFINFO" +FORK=${INFO[1]} +REPO=${INFO[2]} +BRANCH=${INFO[5]} + +git push --set-upstream origin HEAD:$BRANCH + +OPEN=$(echo $(which gnome-open xdg-open open) | awk '{ print $1 }') +URL="https://github.com/$FORK/$REPO/commits/$BRANCH" + +if [ -z "$OPEN" ]; then + echo "Can't find a tool to open the URL:" + echo $URL +else + echo "Viewing commits on $BRANCH..." + "$OPEN" "$URL" +fi diff --git a/buildroot/share/git/ghpc b/buildroot/share/git/ghpc new file mode 100644 index 0000000..a3def15 --- /dev/null +++ b/buildroot/share/git/ghpc @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +# +# ghpc (GitHub Push Current) +# +# - Push current branch to its remote. Try the following until it works: +# - Plain 'git push' +# - 'git push -f' +# - Try the 'git push' command from the 'git push' error message +# - Try adding '-f' to that command +# + +yay() { echo "SUCCESS" ; } +boo() { echo "FAIL" ; } + +FORCE=$([[ "$1" == "--force" || "$1" == "-f" ]] && echo 1) + +if [[ ! $FORCE ]]; then + echo -n "trying 'git push' ...... " + git push >/dev/null 2>&1 && { yay ; exit ; } + boo +fi + +echo -n "trying 'git push -f' ... " + +# Get the error output from the failed push +# and get the recommended 'git push' line +git push -f 2>&1 | { + CMD="" + + ltrim() { + [[ "$1" =~ [^[:space:]].* ]] + printf "%s" "$BASH_REMATCH" + } + + while IFS= read -r line + do + #echo "$line" + if [[ -z "$CMD" && $line =~ "git push" ]]; then + CMD=$(ltrim "$line") + fi + done + + # if a command was found try it + if [[ -n "$CMD" ]]; then + + boo + + if [[ ! $FORCE ]]; then + echo -n "trying '$CMD' ...... " + $CMD >/dev/null 2>&1 && { yay ; exit ; } + boo + fi + + fCMD=${CMD/ push / push -f } + echo -n "trying '$fCMD' ... " + $fCMD >/dev/null 2>&1 && { yay ; exit ; } + boo + + exit 1 + + else + + yay + + fi +} + +[[ ${PIPESTATUS[1]} == 1 ]] && echo "Sorry! Failed to push current branch." diff --git a/buildroot/share/git/ghtp b/buildroot/share/git/ghtp new file mode 100644 index 0000000..14afd8a --- /dev/null +++ b/buildroot/share/git/ghtp @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +# +# ghtp (GitHub Transport Protocol) +# +# Set all remotes in the current repo to HTTPS or SSH connection. +# Useful when switching environments, using public wifi, etc. +# +# Optionally, specify a particular remote to change. +# + +GH_SSH="git@github\.com:" +GH_HTTPS="https:\/\/github\.com\/" + +case "$1" in + -[Hh]) TYPE=HTTPS ; MATCH="git@" ; REPLACE="$GH_SSH/$GH_HTTPS" ;; + -[Ss]) TYPE=SSH ; MATCH="https:" ; REPLACE="$GH_HTTPS/$GH_SSH" ;; + *) + echo "Usage: `basename $0` -h | -s" 1>&2 + echo -e " \e[0;92m-h\e[0m to switch to HTTPS" 1>&2 + echo -e " \e[0;92m-s\e[0m to switch to SSH" 1>&2 + exit 1 + ;; +esac + +AWK=$(which gawk || which awk) + +# Match all or specified remotes of the other type +REGEX="\t$MATCH" ; [[ $# > 1 ]] && REGEX="^$2$REGEX" + +REMOTES=$(git remote -v | egrep "$REGEX" | "$AWK" '{print $1 " " $2}' | sort -u | sed "s/$REPLACE/") + +[[ -z $REMOTES ]] && { echo "Nothing to do." ; exit ; } + +# Update a remote for each results pair +echo "$REMOTES" | xargs -n2 git remote set-url + +echo -n "Remotes set to $TYPE: " +echo "$REMOTES" | "$AWK" '{printf "%s ", $1}' +echo diff --git a/buildroot/share/git/mfadd b/buildroot/share/git/mfadd new file mode 100644 index 0000000..30be1ec --- /dev/null +++ b/buildroot/share/git/mfadd @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +# +# mfadd user[:branch] [copyname] +# +# Add a remote and fetch it. Optionally copy a branch. +# +# Examples: +# mfadd thefork +# mfadd thefork:patch-1 +# mfadd thefork:patch-1 the_patch_12345 +# + +[[ $# > 0 && $# < 3 && $1 != "-h" && $1 != "--help" ]] || { echo "usage: `basename $0` user[:branch] [copyname]" 1>&2 ; exit 1; } + +# If a colon or slash is included, split the parts +if [[ $1 =~ ":" || $1 =~ "/" ]]; then + [[ $1 =~ ":" ]] && IFS=':' || IFS="/" + read -a DATA <<< "$1" + USER=${DATA[0]} + BRANCH=${DATA[1]} + NAME=${2:-$BRANCH} +else + USER=$1 +fi + +MFINFO=$(mfinfo) || exit 1 +IFS=' ' read -a INFO <<< "$MFINFO" +REPO=${INFO[2]} + +set -e + +echo "Adding and fetching $USER..." +git remote add "$USER" "git@github.com:$USER/$REPO.git" >/dev/null 2>&1 || echo "Remote exists." +git fetch "$USER" + +[[ ! -z "$BRANCH" && ! -z "$NAME" ]] && git checkout -b "$NAME" --track "$USER/$BRANCH" diff --git a/buildroot/share/git/mfclean b/buildroot/share/git/mfclean new file mode 100644 index 0000000..fd131c4 --- /dev/null +++ b/buildroot/share/git/mfclean @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +# +# mfclean +# +# Prune all your merged branches and any branches whose remotes are gone +# Great way to clean up your branches after messing around a lot +# + +AWK=$(which gawk || which awk) + +KEEP="RC|RCBugFix|dev|master|bugfix-1|bugfix-2" + +echo "Fetching latest upstream and origin..." +git fetch upstream +git fetch origin +echo + +echo "Pruning Merged Branches..." +git branch --merged | egrep -v "^\*|$KEEP" | xargs -n 1 git branch -d +echo + +echo "Pruning Remotely-deleted Branches..." +git branch -vv | egrep -v "^\*|$KEEP" | grep ': gone]' | "$AWK" '{print $1}' | xargs -n 1 git branch -D +echo + +# List fork branches that don't match local branches +echo "You may want to remove (or checkout) these refs..." +comm -23 \ + <(git branch --all | sed 's/^[\* ] //' | grep origin/ | grep -v "\->" | awk '{ print $1; }' | sed 's/remotes\/origin\///') \ + <(git branch --all | sed 's/^[\* ] //' | grep -v remotes/ | awk '{ print $1; }') \ + | awk '{ print "git branch -d -r origin/" $1; print "git checkout origin/" $1 " -b " $1; print ""; }' +echo diff --git a/buildroot/share/git/mfconfig b/buildroot/share/git/mfconfig new file mode 100644 index 0000000..3fd5e39 --- /dev/null +++ b/buildroot/share/git/mfconfig @@ -0,0 +1,183 @@ +#!/usr/bin/env bash +# +# mfconfig init source dest +# mfconfig manual source dest +# +# The MarlinFirmware/Configurations layout could be broken up into branches, +# but this makes management more complicated and requires more commits to +# perform the same operation, so this uses a single branch with subfolders. +# +# init - Initialize the repo with a base commit and changes: +# - Source will be an 'import' branch containing all current configs. +# - Create an empty 'BASE' branch from 'init-repo'. +# - Add Marlin config files, but reset all to defaults. +# - Commit this so changes will be clear in following commits. +# - Add changed Marlin config files and commit. +# +# manual - Manually import changes from the Marlin repo +# - Replace 'default' configs with those from the Marlin repo. +# - Wait for manual propagation to the rest of the configs. +# - Run init with the given 'source' and 'dest' +# + +REPOHOME="`dirname ~/Projects/Maker/Firmware/.`" +MARLINREPO="$REPOHOME/MarlinFirmware" +CONFIGREPO="$REPOHOME/Configurations" + +CEXA=config/examples +CDEF=config/default +BC=Configuration.h +AC=Configuration_adv.h + +COMMIT_STEPS=0 + +#cd "$CONFIGREPO" 2>/dev/null || { echo "Can't find Configurations repo!" ; exit 1; } + +ACTION=${1:-init} +IMPORT=${2:-"import-2.0.x"} +EXPORT=${3:-"bugfix-2.0.x"} + +echo -n "Doing grhh ... " ; grhh ; echo + +if [[ $ACTION == "manual" ]]; then + + # + # Copy the latest default configs from MarlinFirmware/Marlin + # or one of the import branches here, then use them to construct + # a 'BASE' branch with only defaults as a starting point. + # + + echo "- Updating '$IMPORT' from Marlin..." + + git checkout $IMPORT || exit + + # Reset from the latest complete state + #git reset --hard master + + cp "$MARLINREPO/Marlin/"Configuration*.h "$CDEF/" + #git add . && git commit -m "Changes from Marlin ($(date '+%Y-%m-%d %H:%M'))." + + echo "- Fix up the import branch and come back." + + read -p "- Ready to init [y/N] ?" INIT_YES + echo + + [[ $INIT_YES == 'Y' || $INIT_YES == 'y' ]] || { echo "Done." ; exit ; } + + ACTION='init' +fi + +if [[ $ACTION == "init" ]]; then + # + # Copy all configs from a source such as MarlinFirmware/Marlin + # or one of the import branches here, then use them to construct + # a 'BASE' branch with only defaults as a starting point. + # + + echo "- Initializing BASE branch..." + + # Use the import branch as the source + git checkout $IMPORT || exit + + # Copy to a temporary location + TEMP=$( mktemp -d ) ; cp -R config $TEMP + + # Make sure we're not on the 'BASE' branch... + git checkout master >/dev/null 2>&1 || exit + + # Create 'BASE' as a copy of 'init-repo' (README, LICENSE, etc.) + git branch -D BASE 2>/dev/null + git checkout init-repo -b BASE || exit + + # Copy all config files into place + echo "- Copying configs from Marlin..." + cp -R "$TEMP/config" . + + # Delete anything that's not a Configuration file + find config -type f \! -name "Configuration*" -exec rm "{}" \; + + # DEBUG: Commit the original config files for comparison + ((COMMIT_STEPS)) && git add . >/dev/null && git commit -m "Commit for comparison" >/dev/null + + # Init Cartesian configurations to default + echo "- Initializing configs to default state..." + + find "$CEXA" -name $BC ! -path */delta/* -print0 \ + | while read -d $'\0' F ; do cp "$CDEF/$BC" "$F" ; done + find "$CEXA" -name $AC ! -path */delta/* -print0 \ + | while read -d $'\0' F ; do cp "$CDEF/$AC" "$F" ; done + + # DEBUG: Commit the reset for review + ((COMMIT_STEPS)) && git add . >/dev/null && git commit -m "Reset Cartesian/SCARA configs..." >/dev/null + + # Create base Delta configurations + cp "$CDEF"/* "$CEXA/delta/generic" + + # DEBUG: Commit the reset for review + ((COMMIT_STEPS)) && git add . >/dev/null && git commit -m "Reset Generic Delta..." >/dev/null + + cp -R "$TEMP/$CEXA/delta/generic"/Conf* "$CEXA/delta/generic" + + # DEBUG: Commit Generic Delta changes for review + ((COMMIT_STEPS)) && git add . >/dev/null && git commit -m "Apply Generic Delta..." >/dev/null + + find "$CEXA/delta" -name $BC ! -path */generic/* -print0 \ + | while read -d $'\0' F ; do cp "$CEXA/delta/generic/$BC" "$F" ; done + find "$CEXA/delta" -name $AC ! -path */generic/* -print0 \ + | while read -d $'\0' F ; do cp "$CEXA/delta/generic/$AC" "$F" ; done + + # DEBUG: Commit the reset for review + ((COMMIT_STEPS)) && git add . >/dev/null && git commit -m "Reset Delta configs..." >/dev/null + + # SCARA configurations + find "$CEXA/SCARA" -name $BC \ + | while read -d $'\0' F ; do cp "$CDEF/$BC" "$F" ; done + find "$CEXA/SCARA" -name $AC \ + | while read -d $'\0' F ; do cp "$CDEF/$AC" "$F" ; done + + # DEBUG: Commit the reset for review or... + ((COMMIT_STEPS)) && git add . >/dev/null && git commit -m "Reset SCARA..." >/dev/null + + # Update the %VERSION% in the README.md file + SED=$(which gsed || which sed) + VERS=$( echo $EXPORT | $SED 's/release-//' ) + eval "${SED} -E -i~ -e 's/%VERSION%/$VERS/g' README.md" + rm -f README.md~ + + # NOT DEBUGGING: Commit the 'BASE', ready for customizations + ((COMMIT_STEPS)) || git add . >/dev/null && git commit --amend --no-edit >/dev/null + + # Create a new branch from 'BASE' for the final result + echo "- Creating '$EXPORT' branch for the result..." + git branch -D $EXPORT 2>/dev/null + git checkout -b $EXPORT || exit + + # Delete temporary branch + git branch -D BASE 2>/dev/null + + echo "- Applying example config customizations..." + cp -R "$TEMP/config" . + find config -type f \! -name "Configuration*" -exec rm "{}" \; + + echo "- Adding path labels to all configs..." + config-labels.py >/dev/null 2>&1 + + git add . >/dev/null && git commit -m "Examples Customizations" >/dev/null + + echo "- Copying extras from Marlin..." + cp -R "$TEMP/config" . + + # Apply labels again! + config-labels.py >/dev/null 2>&1 + + git add . >/dev/null && git commit -m "Examples Extras" >/dev/null + + rm -rf $TEMP + + git push -f --set-upstream upstream "$EXPORT" + +else + + echo "Usage: mfconfig init|manual|rebase" + +fi diff --git a/buildroot/share/git/mfdoc b/buildroot/share/git/mfdoc new file mode 100644 index 0000000..ce21419 --- /dev/null +++ b/buildroot/share/git/mfdoc @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +# +# mfdoc +# +# Start Jekyll in watch mode to work on Marlin Documentation and preview locally +# + +[[ $# == 0 ]] || { echo "Usage: `basename $0`" 1>&2 ; exit 1; } + +MFINFO=$(mfinfo "$@") || exit 1 +IFS=' ' read -a INFO <<< "$MFINFO" +ORG=${INFO[0]} +REPO=${INFO[2]} +BRANCH=${INFO[5]} + +[[ $ORG == "MarlinFirmware" && $REPO == "MarlinDocumentation" ]] || { echo "Wrong repository." 1>&2; exit 1; } + +opensite() { + URL="http://127.0.0.1:4000/" + OPEN=$(echo $(which gnome-open xdg-open open) | awk '{ print $1 }') + if [ -z "$OPEN" ]; then + echo "Can't find a tool to open the URL:" + echo $URL + else + echo "Opening preview site in the browser..." + "$OPEN" "$URL" + fi +} + +echo "Previewing MarlinDocumentation..." + +bundle exec jekyll serve --watch --incremental | { + while IFS= read -r line + do + [[ $line =~ "Server running" ]] && opensite + echo "$line" + done +} diff --git a/buildroot/share/git/mffp b/buildroot/share/git/mffp new file mode 100644 index 0000000..2663a4a --- /dev/null +++ b/buildroot/share/git/mffp @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +# +# mffp [1|2|3] [commit-id] +# +# Push the given commit (or HEAD) upstream immediately. +# By default: `git push upstream HEAD:bugfix-1.1.x` +# + +[[ $# < 3 && $1 != "-h" && $1 != "--help" ]] || { echo "usage: `basename $0` [1|2|3] [commit-id]" 1>&2 ; exit 1; } + +if [[ $1 == '1' || $1 == '2' || $1 == '3' ]]; then + MFINFO=$(mfinfo "$1") || exit 1 + REF=${2:-HEAD} +else + MFINFO=$(mfinfo) || exit 1 + REF=${1:-HEAD} +fi + +IFS=' ' read -a INFO <<< "$MFINFO" +ORG=${INFO[0]} +TARG=${INFO[3]} + +if [[ $ORG == "MarlinFirmware" ]]; then + git push upstream $REF:$TARG +else + echo "Not a MarlinFirmware working copy."; exit 1 +fi diff --git a/buildroot/share/git/mfhelp b/buildroot/share/git/mfhelp new file mode 100644 index 0000000..1afc4c6 --- /dev/null +++ b/buildroot/share/git/mfhelp @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# +# mfhelp +# + +cat </dev/null | grep ^* | sed 's/\* //g') +[[ -z $CURR ]] && { echo "No git repository here!" 1>&2 ; exit 1; } +[[ $CURR == "(no"* ]] && { echo "Git is busy with merge, rebase, etc." 1>&2 ; exit 1; } + +REPO=$(git remote get-url upstream 2>/dev/null | sed -E 's/.*\/(.*)\.git/\1/') +[[ -z $REPO ]] && { echo "`basename $0`: No 'upstream' remote found. (Did you run mfinit?)" 1>&2 ; exit 1; } + +ORG=$(git remote get-url upstream 2>/dev/null | sed -E 's/.*[\/:](.*)\/.*$/\1/') +[[ $ORG == MarlinFirmware ]] || { echo "`basename $0`: Not a Marlin repository." 1>&2 ; exit 1; } + +FORK=$(git remote get-url origin 2>/dev/null | sed -E 's/.*[\/:](.*)\/.*$/\1/') + +# Defaults if no arguments given +BRANCH=$CURR +INDEX=1 + +while [[ $# -gt 0 ]]; do + opt="$1" ; shift ; val="$1" + + IFS='=' read -a PARTS <<<"$opt" + [[ "${PARTS[1]}" != "" ]] && { EQUALS=1 ; opt="${PARTS[0]}" ; val="${PARTS[1]}" ; } + + GOODVAL=1 + if [[ "$val" =~ ^-{1,2}.* || ! "$opt" =~ ^-{1,2}.* ]]; then + GOODVAL=0 + val="" + fi + + case "$opt" in + -*|--*) MORE="$MORE$opt " ; [[ $EQUALS == 1 ]] && MORE="$MORE=$val" ;; + 1|2|3) INDEX=$opt ;; + *) BRANCH="$opt" ;; + esac + +done + +case "$REPO" in + Marlin ) TARG=bugfix-2.0.x ; [[ $INDEX == 1 ]] && TARG=bugfix-1.1.x ; [[ $INDEX == 3 ]] && TARG=dev-2.1.x ;; + Configurations ) TARG=import-2.0.x ;; + MarlinDocumentation ) TARG=master ;; +esac + +[[ $BRANCH =~ ^[123]$ ]] && USAGE=1 + +[[ $USAGE == 1 ]] && { echo "usage: `basename $0` [1|2|3] [branch]" 1>&2 ; exit 1 ; } + +echo "$ORG $FORK $REPO $TARG $BRANCH $CURR $MORE" diff --git a/buildroot/share/git/mfinit b/buildroot/share/git/mfinit new file mode 100644 index 0000000..bc2495a --- /dev/null +++ b/buildroot/share/git/mfinit @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# +# mfinit +# +# Create the upstream remote for a forked repository +# + +[[ $# == 0 ]] || { echo "usage: `basename $0`" 1>&2 ; exit 1; } + +[[ -z $(git branch 2>/dev/null | grep ^* | sed 's/\* //g') ]] && { echo "No git repository here!" 1>&2 ; exit 1; } + +REPO=$(git remote get-url origin 2>/dev/null | sed -E 's/.*\/(.*)\.git/\1/') +[[ -z $REPO ]] && { echo "`basename $0`: No 'origin' remote found." 1>&2 ; exit 1; } + +echo "Adding 'upstream' remote for convenience." +git remote add upstream "git@github.com:MarlinFirmware/$REPO.git" +git fetch upstream diff --git a/buildroot/share/git/mfnew b/buildroot/share/git/mfnew new file mode 100644 index 0000000..e491fea --- /dev/null +++ b/buildroot/share/git/mfnew @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +# +# mfnew +# +# Create a new branch from the default target with the given name +# + +usage() { + echo "usage: `basename $0` [1|2|3] [name]" 1>&2 +} + +[[ $# < 3 && $1 != "-h" && $1 != "--help" ]] || { usage; exit 1; } + +MFINFO=$(mfinfo "$@") || exit 1 +IFS=' ' read -a INFO <<< "$MFINFO" +TARG=${INFO[3]} +BRANCH=pr_for_$TARG-$(date +"%G-%m-%d_%H.%M.%S") + +# BRANCH can be given as the last argument +case "$#" in + 1 ) case "$1" in + 1|2|3) ;; + *) BRANCH=$1 ;; + esac + ;; + 2 ) case "$1" in + 1|2|3) BRANCH=$2 ;; + *) usage ; exit 1 ;; + esac + ;; +esac + +git fetch upstream +git checkout --no-track upstream/$TARG -b $BRANCH diff --git a/buildroot/share/git/mfpr b/buildroot/share/git/mfpr new file mode 100644 index 0000000..b853c69 --- /dev/null +++ b/buildroot/share/git/mfpr @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +# +# mfpr [1|2|3] +# +# Make a PR against bugfix-1.1.x or bugfix-2.0.x +# + +[[ $# < 2 && $1 != "-h" && $1 != "--help" ]] || { echo "usage: `basename $0` [1|2|3] [branch]" 1>&2 ; exit 1; } + +MFINFO=$(mfinfo "$@") || exit 1 +IFS=' ' read -a INFO <<< "$MFINFO" +ORG=${INFO[0]} +FORK=${INFO[1]} +REPO=${INFO[2]} +TARG=${INFO[3]} +BRANCH=${INFO[4]} +OLDBRANCH=${INFO[5]} + +[[ $BRANCH == $TARG ]] && { echo "Can't create a PR from the PR Target ($BRANCH). Make a copy first." 1>&2 ; exit 1; } + +[[ $BRANCH != $OLDBRANCH ]] && { git checkout $BRANCH || exit 1; } + +# See if it's been pushed yet +if [ -z "$(git branch -vv | grep ^\* | grep \\[origin)" ]; then firstpush; fi + +OPEN=$(echo $(which gnome-open xdg-open open) | awk '{ print $1 }') +URL="https://github.com/$ORG/$REPO/compare/$TARG...$FORK:$BRANCH?expand=1" + +if [ -z "$OPEN" ]; then + echo "Can't find a tool to open the URL:" + echo $URL +else + echo "Opening a New PR Form..." + "$OPEN" "$URL" +fi + +[[ $BRANCH != $OLDBRANCH ]] && git checkout $OLDBRANCH diff --git a/buildroot/share/git/mfpub b/buildroot/share/git/mfpub new file mode 100644 index 0000000..6a912e5 --- /dev/null +++ b/buildroot/share/git/mfpub @@ -0,0 +1,138 @@ +#!/usr/bin/env bash +# +# mfpub +# +# Use Jekyll to generate Marlin Documentation, which is then +# git-pushed to Github to publish it to the live site. +# This publishes the current branch, and doesn't force +# changes to be pushed to the 'master' branch. Be sure to +# push any permanent changes to 'master'. +# + +[[ $# < 2 && $1 != "-h" && $1 != "--help" ]] || { echo "Usage: `basename $0` [branch]" 1>&2 ; exit 1; } + +MFINFO=$(mfinfo "$@") || exit 1 +IFS=' ' read -a INFO <<< "$MFINFO" +ORG=${INFO[0]} +FORK=${INFO[1]} +REPO=${INFO[2]} +TARG=${INFO[3]} +BRANCH=${INFO[4]} +CURR=${INFO[5]} + +if [[ $ORG != "MarlinFirmware" || $REPO != "MarlinDocumentation" ]]; then + echo "Wrong repository." + exit +fi + +if [[ $BRANCH == "gh-pages" ]]; then + echo "Can't build from 'gh-pages.' Only the Jekyll branches (based on 'master')." + exit +fi + +# Check out the named branch (or stay in current) + +if [[ $BRANCH != $CURR ]]; then + echo "Stashing any changes to files..." + [[ $(git stash) != "No local "* ]] && HAS_STASH=1 + git checkout $BRANCH +fi + +COMMIT=$( git log --format="%H" -n 1 ) + +# Clean out changes and other junk in the branch +git clean -d -f + +opensite() { + URL="$1" + OPEN=$(echo $(which gnome-open xdg-open open) | awk '{ print $1 }') + if [ -z "$OPEN" ]; then + echo "Can't find a tool to open the URL:" + echo $URL + else + echo "Opening the site in the browser..." + "$OPEN" "$URL" + fi +} + +# Push 'master' to the fork and make a proper PR... +if [[ $BRANCH == $TARG ]]; then + + # Don't lose upstream changes! + git fetch upstream + + # Rebase onto latest master + if git rebase upstream/$TARG; then + + # Allow working directly with the main fork + echo + echo -n "Pushing to origin/$TARG... " + git push origin HEAD:$TARG + + echo + echo -n "Pushing to upstream/$TARG... " + git push upstream HEAD:$TARG + + else + + echo "Merge conflicts? Stopping here." + exit + + fi + +else + + if [ -z "$(git branch -vv | grep ^\* | grep \\\[origin)" ]; then + firstpush + else + echo + echo -n "Pushing to origin/$BRANCH... " + git push -f origin + fi + + opensite "https://github.com/$ORG/$REPO/compare/$TARG...$FORK:$BRANCH?expand=1" + +fi + +# Uncomment to compress the final html files +# mv ./_plugins/jekyll-press.rb-disabled ./_plugins/jekyll-press.rb +# bundle install + +echo +echo "Generating MarlinDocumentation..." + +rm -rf build + +# build the site statically and proof it +bundle exec jekyll build --profile --trace --no-watch +bundle exec htmlproofer ./build --only-4xx --allow-hash-href --check-favicon --check-html --url-swap ".*marlinfw.org/:/" + +# Sync the built site into a temporary folder +TMPFOLDER=$( mktemp -d ) +rsync -av build/ ${TMPFOLDER}/ + +# Clean out changes and other junk in the branch +git reset --hard +git clean -d -f + +# Copy built-site into the gh-pages branch +git checkout gh-pages || { echo "Something went wrong!"; exit 1; } +rsync -av ${TMPFOLDER}/ ./ + +# Commit and push the new live site directly +git add --all +git commit --message "Built from ${COMMIT}" +git push -f origin +git push -f upstream | { + while IFS= read -r line + do + [[ $line =~ "gh-pages -> gh-pages" ]] && opensite "https://marlinfw.org/" + echo "$line" + done +} + +# remove the temporary folder +rm -rf ${TMPFOLDER} + +# Go back to the branch we started from +git checkout $CURR && [[ $HAS_STASH == 1 ]] && git stash pop diff --git a/buildroot/share/git/mfqp b/buildroot/share/git/mfqp new file mode 100644 index 0000000..5650d08 --- /dev/null +++ b/buildroot/share/git/mfqp @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +# +# mfqp [1|2|3] +# +# - git add . +# - git commit --amend +# - ghpc +# + +MFINFO=$(mfinfo "$@") || exit 1 +IFS=' ' read -a INFO <<< "$MFINFO" +REPO=${INFO[2]} +TARG=${INFO[3]} +CURR=${INFO[5]} + +IND=6 +while [ $IND -lt ${#INFO[@]} ]; do + ARG=${INFO[$IND]} + case "$ARG" in + -h|--help ) USAGE=1 ;; + * ) USAGE=1 ; echo "unknown option: $ARG" ;; + esac + let IND+=1 +done + +[[ $USAGE == 1 ]] && { echo "usage: `basename $0` [1|2|3]" 1>&2 ; exit 1 ; } + +[[ $CURR == $TARG && $REPO != "MarlinDocumentation" ]] && { echo "Don't alter the PR Target branch."; exit 1 ; } + +git add . && git commit --amend && git push -f diff --git a/buildroot/share/git/mfrb b/buildroot/share/git/mfrb new file mode 100644 index 0000000..071b0b3 --- /dev/null +++ b/buildroot/share/git/mfrb @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +# +# mfrb +# +# Do "git rebase -i" against the "target" branch (bugfix-1.1.x, bugfix-2.0.x, dev-2.1.x, or master) +# + +MFINFO=$(mfinfo "$@") || exit 1 +IFS=' ' read -a INFO <<< "$MFINFO" +TARG=${INFO[3]} +CURR=${INFO[5]} + +IND=6 +while [ $IND -lt ${#INFO[@]} ]; do + ARG=${INFO[$IND]} + case "$ARG" in + -q|--quick ) QUICK=1 ;; + -h|--help ) USAGE=1 ;; + * ) USAGE=1 ; echo "unknown option: $ARG" ;; + esac + let IND+=1 +done + +[[ $USAGE == 1 ]] && { echo "usage: `basename $0` [1|2|3]" 1>&2 ; exit 1 ; } + +[[ $QUICK ]] || git fetch upstream +git rebase upstream/$TARG && git rebase -i upstream/$TARG diff --git a/buildroot/share/git/mftest b/buildroot/share/git/mftest new file mode 100644 index 0000000..11d0ac3 --- /dev/null +++ b/buildroot/share/git/mftest @@ -0,0 +1,306 @@ +#!/usr/bin/env bash +# +# mftest Select a test to apply and build +# mftest -b [#] Build the auto-detected environment +# mftest -u [#] Upload the auto-detected environment +# mftest [name] [index] [-y] Set config options and optionally build a test +# + +MFINFO=$(mfinfo) || exit 1 +[[ -d Marlin/src ]] || { echo "Please 'cd' to the Marlin repo root." ; exit 1 ; } + +perror() { echo -e "$0: \033[0;31m$1 -- $2\033[0m" ; } +errout() { echo -e "\033[0;31m$1\033[0m" ; } +bugout() { ((DEBUG)) && echo -e "\033[0;32m$1\033[0m" ; } + +usage() { + echo " +Usage: mftest [-t|--env=] [-n|--num=] [-m|--make] [-y|--build=] + mftest [-a|--autobuild] + mftest [-r|--rebuild] + mftest [-u|--autoupload] [-n|--num=] + +OPTIONS + -t --env The environment of the test to apply / run. (As named in platformio.ini.) + -n --num The index of the test to run. (In *-tests file order.) + -m --make Use the make / Docker method for the build. + -y --build Skip 'Do you want to build this test?' and assume YES. + -h --help Print this help. + -a --autobuild PIO Build using the MOTHERBOARD environment. + -u --autoupload PIO Upload using the MOTHERBOARD environment. + -v --verbose Extra output for debugging. + +env shortcuts: tree due esp lin lpc|lpc8 lpc9 m128 m256|mega stm|f1 f4 f7 s6 teensy|t31|t32 t35|t36 t40|t41 + +" +} + +TESTPATH=buildroot/tests + +STATE_FILE=$( echo ./.pio/.mftestrc ) +SED=$(which gsed || which sed) + +shopt -s extglob nocasematch + +# Matching patterns +ISNUM='^[0-9]+$' +ISCMD='^(restore|opt|exec|use|pins|env)_' +ISEXEC='^exec_' +ISCONT='\\ *$' + +# Get environment, test number, etc. from the command +TESTENV='-' +CHOICE=0 +DEBUG=0 + +while getopts 'abhmruvyn:t:-:' OFLAG; do + case "${OFLAG}" in + a) AUTO_BUILD=1 ; bugout "Auto-Build target..." ;; + h) EXIT_USAGE=1 ;; + m) USE_MAKE=1 ; bugout "Using make with Docker..." ;; + n) case "$OPTARG" in + *[!0-9]*) perror "option requires a number" $OFLAG ; EXIT_USAGE=1 ;; + *) CHOICE="$OPTARG" ; bugout "Got a number: $CHOICE" ;; + esac + ;; + r) REBUILD=1 ; bugout "Rebuilding previous..." ;; + t) TESTENV="$OPTARG" ; bugout "Got a target: $TESTENV" ;; + u) AUTO_BUILD=2 ; bugout "Auto-Upload target..." ;; + v) DEBUG=1 ; bugout "Debug ON" ;; + y) BUILD_YES='Y' ; bugout "Build will initiate..." ;; + -) IFS="=" read -r ONAM OVAL <<< "$OPTARG" + case "$ONAM" in + help) [[ -z "$OVAL" ]] || perror "option can't take value $OVAL" $ONAM ; EXIT_USAGE=1 ;; + autobuild) AUTO_BUILD=1 ; bugout "Auto-Build target..." ;; + autoupload) AUTO_BUILD=2 ; bugout "Auto-Upload target..." ;; + env) case "$OVAL" in + '') perror "option requires a value" $ONAM ; EXIT_USAGE=1 ;; + *) TESTENV="$OVAL" ; bugout "Got a target: $TESTENV" ;; + esac + ;; + num) case "$OVAL" in + [0-9]+) CHOICE="$OVAL" ; bugout "Got a number: $CHOICE" ;; + *) perror "option requires a value" $ONAM ; EXIT_USAGE=1 ;; + esac + ;; + rebuild) REBUILD=1 ; bugout "Rebuilding previous..." ;; + make) USE_MAKE=1 ; bugout "Using make with Docker..." ;; +debug|verbose) DEBUG=1 ; bugout "Debug ON" ;; + build) case "$OVAL" in + ''|y|yes) BUILD_YES='Y' ;; + n|no) BUILD_YES='N' ;; + *) perror "option value must be y, n, yes, or no" $ONAM ; EXIT_USAGE=1 ;; + esac + bugout "Build will initiate? ($BUILD_YES)" + ;; + *) perror "Unknown flag" "$OPTARG" ; EXIT_USAGE=1 ;; + esac + ;; + esac +done + +((EXIT_USAGE)) && { usage ; exit 1 ; } + +if ((REBUILD)); then + bugout "Rebuilding previous..." + # Build with the last-built env + [[ -f "$STATE_FILE" ]] || { errout "No previous (-r) build state found." ; exit 1 ; } + read TESTENV <"$STATE_FILE" + pio run -d . -e $TESTENV + exit +fi + +case $TESTENV in + tree) pio run -d . -e include_tree ; exit 1 ;; + due) TESTENV='DUE' ;; + esp) TESTENV='esp32' ;; + lin*) TESTENV='linux_native' ;; +lpc?(8)) TESTENV='LPC1768' ;; + lpc9) TESTENV='LPC1769' ;; + m128) TESTENV='mega1280' ;; + m256) TESTENV='mega2560' ;; + mega) TESTENV='mega2560' ;; + stm) TESTENV='STM32F103RE' ;; + f1) TESTENV='STM32F103RE' ;; + f4) TESTENV='STM32F4' ;; + f7) TESTENV='STM32F7' ;; + s6) TESTENV='FYSETC_S6' ;; + teensy) TESTENV='teensy31' ;; + t31) TESTENV='teensy31' ;; + t32) TESTENV='teensy31' ;; + t35) TESTENV='teensy35' ;; + t36) TESTENV='teensy35' ;; + t40) TESTENV='teensy41' ;; + t41) TESTENV='teensy41' ;; +esac + +if ((AUTO_BUILD)); then + # + # List environments that apply to the current MOTHERBOARD. + # + case $(uname | tr '[:upper:]' '[:lower:]') in + darwin) SYS='mac' ;; + *linux) SYS='lin' ;; + win*) SYS='win' ;; + msys*) SYS='win' ;; + cygwin*) SYS='win' ;; + mingw*) SYS='win' ;; + *) SYS='uni' ;; + esac + echo ; echo -n "Auto " ; ((AUTO_BUILD == 2)) && echo "Upload..." || echo "Build..." + MB=$( grep -E "^\s*#define MOTHERBOARD" Marlin/Configuration.h | awk '{ print $3 }' | $SED 's/BOARD_//' ) + [[ -z $MB ]] && { echo "Error - Can't read MOTHERBOARD setting." ; exit 1 ; } + BLINE=$( grep -E "define\s+BOARD_$MB\b" Marlin/src/core/boards.h ) + BNUM=$( $SED -E 's/^.+BOARD_[^ ]+ +([0-9]+).+$/\1/' <<<"$BLINE" ) + BDESC=$( $SED -E 's/^.+\/\/ *(.+)$/\1/' <<<"$BLINE" ) + [[ -z $BNUM ]] && { echo "Error - Can't find $MB in boards list." ; exit 1 ; } + ENVS=( $( grep -EA1 "MB\(.*\b$MB\b.*\)" Marlin/src/pins/pins.h | grep -E "#include.+//.+(env|$SYS):[^ ]+" | grep -oE "(env|$SYS):[^ ]+" | $SED -E "s/(env|$SYS)://" ) ) + [[ -z $ENVS ]] && { errout "Error - Can't find target(s) for $MB ($BNUM)." ; exit 1 ; } + ECOUNT=${#ENVS[*]} + + if [[ $ECOUNT == 1 ]]; then + TARGET=$ENVS + else + if [[ $CHOICE == 0 ]]; then + # List env names and numbers. Get selection. + echo "Available targets for \"$BDESC\" | $MB ($BNUM):" + + IND=0 ; for ENV in "${ENVS[@]}"; do let IND++ ; echo " $IND) $ENV" ; done + + if [[ $ECOUNT > 1 ]]; then + for (( ; ; )) + do + read -p "Select a target for '$MB' (1-$ECOUNT) : " CHOICE + [[ -z "$CHOICE" ]] && { echo '(canceled)' ; exit 1 ; } + [[ $CHOICE =~ $ISNUM ]] && ((CHOICE >= 1 && CHOICE <= ECOUNT)) && break + errout ">>> Invalid environment choice '$CHOICE'." + done + echo + fi + else + echo "Detected \"$BDESC\" | $MB ($BNUM)." + [[ $CHOICE > $ECOUNT ]] && { echo "Environment selection out of range." ; exit 1 ; } + fi + TARGET="${ENVS[$CHOICE-1]}" + echo "Selected $TARGET" + fi + + echo "$TARGET" >"$STATE_FILE" + + if ((AUTO_BUILD == 2)); then + echo "Uploading environment $TARGET for board $MB ($BNUM)..." ; echo + pio run -t upload -e $TARGET + else + echo "Building environment $TARGET for board $MB ($BNUM)..." ; echo + pio run -e $TARGET + fi + exit +fi + +# +# List available tests and ask for selection +# + +if [[ $TESTENV == '-' ]]; then + IND=0 + NAMES=() + for FILE in $( ls -1 $TESTPATH/*-tests ) + do + let IND++ + TNAME=${FILE/-tests/} + TNAME=${TNAME/$TESTPATH\//} + NAMES+=($TNAME) + (( IND < 10 )) && echo -n " " + echo " $IND) $TNAME" + done + + echo + for (( ; ; )) + do + read -p "Select a test to apply (1-$IND) : " NAMEIND + [[ -z "$NAMEIND" ]] && { errout "(canceled)" ; exit 1 ; } + [[ $NAMEIND =~ $ISNUM ]] && ((NAMEIND >= 1 && NAMEIND <= IND)) && { TESTENV=${NAMES[$NAMEIND-1]} ; echo ; break ; } + errout "Invalid selection." + done +fi + +# Get the contents of the test file +OUT=$( cat $TESTPATH/$TESTENV-tests 2>/dev/null ) || { errout "Can't find test '$TESTENV'." ; exit 1 ; } + +# Count up the number of tests +TESTCOUNT=$( awk "/$ISEXEC/{a++}END{print a}" <<<"$OUT" ) + +# User entered a number? +(( CHOICE && CHOICE > TESTCOUNT )) && { errout "Invalid test selection '$CHOICE' (1-$TESTCOUNT)." ; exit 1 ; } + +if [[ $CHOICE == 0 ]]; then + # + # List test descriptions with numbers and get selection + # + echo "Available '$TESTENV' tests:" ; echo "$OUT" | { + IND=0 + while IFS= read -r LINE + do + if [[ $LINE =~ $ISEXEC ]]; then + DESC=$( "$SED" -E 's/^exec_test \$1 \$2 "([^"]+)".*$/\1/g' <<<"$LINE" ) + (( ++IND < 10 )) && echo -n " " + echo " $IND) $DESC" + fi + done + } + CHOICE=1 + if [[ $TESTCOUNT > 1 ]]; then + for (( ; ; )) + do + read -p "Select a '$TESTENV' test (1-$TESTCOUNT) : " CHOICE + [[ -z "$CHOICE" ]] && { errout "(canceled)" ; exit 1 ; } + [[ $CHOICE =~ $ISNUM ]] && ((CHOICE >= 1 && CHOICE <= TESTCOUNT)) && break + errout ">>> Invalid test selection '$CHOICE'." + done + fi +fi + +# +# Run the specified test lines +# +echo -ne "\033[0;33m" +echo "$OUT" | { + IND=0 + GOTX=0 + CMD="" + while IFS= read -r LINE + do + if [[ $LINE =~ $ISCMD || $GOTX == 1 ]]; then + ((!IND)) && let IND++ + if [[ $LINE =~ $ISEXEC ]]; then + ((IND++ > CHOICE)) && break + else + ((!HEADER)) && { + HEADER=1 + echo -e "\n#\n# Test $TESTENV ($CHOICE) $DESC\n#" + } + ((IND == CHOICE)) && { + GOTX=1 + [[ $CMD == "" ]] && CMD="$LINE" || CMD=$( echo -e "$CMD$LINE" | $SED -e 's/\\//g' | $SED -E 's/ +/ /g' ) + [[ $LINE =~ $ISCONT ]] || { echo "$CMD" ; eval "$CMD" ; CMD="" ; } + } + fi + fi + done +} +echo -ne "\033[0m" + +# Make clear it's a TEST +opt_set CUSTOM_MACHINE_NAME "\"$TESTENV-tests ($CHOICE)\"" + +# Build the test too? +if [[ -z "$BUILD_YES" ]]; then + echo + read -p "Build $TESTENV test #$CHOICE (y/N) ? " BUILD_YES +fi + +[[ $BUILD_YES == 'Y' || $BUILD_YES == 'Yes' ]] && { + ((USE_MAKE)) && make tests-single-local TEST_TARGET=$TESTENV ONLY_TEST=$CHOICE + ((USE_MAKE)) || pio run -d . -e $TESTENV + echo "$TESTENV" >"$STATE_FILE" +} diff --git a/buildroot/share/git/mfup b/buildroot/share/git/mfup new file mode 100644 index 0000000..691259e --- /dev/null +++ b/buildroot/share/git/mfup @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +# +# mfup +# +# - Fetch latest upstream and replace the PR Target branch with +# - Rebase the (current or specified) branch on the PR Target +# - Force-push the branch to 'origin' +# + +[[ $# < 3 && $1 != "-h" && $1 != "--help" ]] || { echo "usage: `basename $0` [1|2] [branch]" 1>&2 ; exit 1; } + +MFINFO=$(mfinfo "$@") || exit 1 +IFS=' ' read -a INFO <<< "$MFINFO" +ORG=${INFO[0]} +FORK=${INFO[1]} +REPO=${INFO[2]} +TARG=${INFO[3]} +BRANCH=${INFO[4]} +CURR=${INFO[5]} + +set -e + +# Prevent accidental loss of current changes +[[ $(git stash) != "No local "* ]] && HAS_STASH=1 + +echo "Fetching upstream ($ORG/$REPO)..." +git fetch upstream + +if [[ $BRANCH != $TARG ]]; then + echo ; echo "Rebasing $BRANCH on $TARG..." + if [[ $BRANCH == $CURR ]] || git checkout $BRANCH; then + if git rebase upstream/$TARG; then + git push -f + else + echo "Looks like merge conflicts. Stopping here." + exit + fi + else + echo "No such branch!" + fi +else + git reset --hard upstream/$TARG +fi + +echo +[[ $BRANCH != $CURR ]] && git checkout $CURR + +[[ $HAS_STASH == 1 ]] && git stash pop -- cgit v1.2.3