"upstream.sh" Script To Rebase Changes - FujiNetWIFI/fujinet-firmware GitHub Wiki

TL;DR:

Run the following before starting new work, or after you have made changes in a branch and want to rebase your work on top of new changes:

$ upstream.sh

Then you can retest and push your work to your fork.

The full version

Small warning: Yes, git can go awry sometimes when you use scripts like this, as they generally assume the previous steps worked fine, and continue to do probably bad things making it worse. Please ensure you know what you're doing before running any scripts that do things like I'm doing below. If you're worried about your work, consider manually performing the steps and learning how it all works before diving in with this.

I offer the following as an example of a script that can do a lot of the grunt work of rebasing your changes for you.

It can do the following:

  • Pull latest changes from master/main/a-named-branch in the remote into your own repository
  • Rebase your changes on top of the current HEAD from the remote, from either working on a local branch (as described in the guide) or on master itself

It has some prerequisites, that match the information in The Guidelines

  1. You have forked the repository you are working against
  2. You have create an "upstream" remote pointing to the main repository you forked from

Example usage

Let's say I haven't pulled changes from upstream for a while and I'm about to do some new work.

$ git hist 3
* 0ea99c53 2023-12-18 | httpbin cross platform application using network library to show how to use it (HEAD -> master, origin/master, origin/httpbin-cross-pf, origin/HEAD, httpbin-cross-pf) [Mark Fisher]
* c77a27d8 2023-12-16 | oops... put those back [idolpx]
*   2a0a2456 2023-12-16 | Merge commit '82c96e3eda2f8009e53076524c92d57f4d0f4fdf' [idolpx]

I can first run upstream.sh to fetch those changes and apply them to my local repository.

$ upstream.sh
Fetching upstream changes
From github.com:FujiNetWIFI/fujinet-apps
 * branch              master     -> FETCH_HEAD
Merging changes (fast forward only)
Updating 0ea99c53..dc641090
Fast-forward
 .gitignore                                            |   3 +-
 COMMON/include/macros.inc                             | 280 +++...+++++
 COMMON/include/zp.inc                                 |  12 ++++++
 GAMEIDEAS.md                                          |  52 ++++...++
 ... etc.

Git has reported that it has pulled the changes from upstream and moved me from commit 0ea99c53 (which we can see was my latest commit when I typed git hist), to dc641090 which is the current upstream's latest change.

I can now check the git status to see where I've got to:

$ git hist 3
* dc641090 2023-12-29 | Talk to my site via HTTPS; new version bump (HEAD -> master, tag: apod-2023-12-29, upstream/master) [Bill Kendrick]
*   257d5683 2023-12-29 | Merge branch 'master' of https://github.com/FujiNetWIFI/fujinet-apps [Bill Kendrick]
|\
| *   a241e9ac 2023-12-28 | Merge commit 'a853c6a7a0eb343c355428dbe40218498744558b' [idolpx]

$ git status
On branch master
Your branch is ahead of 'origin/master' by 12 commits.
  (use "git push" to publish your local commits)

nothing to commit, working tree clean

This tells me my local fork is up to date, git hist 3 shows the top commit as it mentioned is "dc641090", and it is "upstream/master" (see end of the line) meaning it is where the "master" branch is pointing to on the "upstream" remote (short hand "upstream/master").

It also tells me that I am ahead of "origin/master" by 12 commits. "origin" is the name of the remote for your fork. So "origin/master" is the short hand form of "the origin remote, the master branch".

Thus we should push our changes to our fork. We have them locally, but not in github yet.

$ git push
Total 0 (delta 0), reused 0 (delta 0), pack-reused 0
To github.com:markjfisher/fujinet-apps.git
   0ea99c53..dc641090  master -> master

You're now up to date locally, and can follow the guideline to create a new branch for your work, do the development.

Rebasing your work

After completing your local changes for your uber-leet new feature, and before you push your changes, you can also use the script to rebase your changes with any incoming changes that someone else has pushed to the repository. This is described in the guide, but the upstream.sh script can do that for you too.

Let's pretend for a second that when I started work, the last commit was 0ea99c53, as we previously saw with git hist.

I've now made my change as a single commit in a branch called xp-ipify, but I need to rebase it with external changes.

Once again, upstream.sh to the rescue:

$ git hist 2
* 248f743d 2023-12-30 | Add ipify app to test network-lib json functions (HEAD -> xp-ipify) [Mark Fisher]
* 0ea99c53 2023-12-18 | httpbin cross platform application using network library to show how to use it (origin/master, origin/httpbin-cross-pf, origin/HEAD, master, httpbin-cross-pf) [Mark Fisher]

we can see the change on top of the commit 0ea99c53, but we need to rebase it (i.e. move it) onto the top of the current head of the repository:

$ upstream.sh
Changing from branch xp-ipify to master
Switched to branch 'master'
Your branch is up to date with 'origin/master'.
Fetching upstream changes
From github.com:FujiNetWIFI/fujinet-apps
 * branch              master     -> FETCH_HEAD
Merging changes (fast forward only)
Updating 0ea99c53..dc641090
Fast-forward
 .gitignore                                            |   3 +-
 COMMON/include/macros.inc                             | 280 ++++...+++
 COMMON/include/zp.inc                                 |  12 ++++++
 ... other changes

Resetting back to previous branch xp-ipify
Switched to branch 'xp-ipify'
Rebasing local changes with external. You may need to fix merge conflicts
Successfully rebased and updated refs/heads/xp-ipify.

It pulled and merged the upstream changes into our master, then rebased our work on top of that.

We can now view the history and see our commit is on top of the current upstream/master:

$ git hist 3
* a94ba9cc 2023-12-30 | Add ipify app to test network-lib json functions (HEAD -> xp-ipify) [Mark Fisher]
* dc641090 2023-12-29 | Talk to my site via HTTPS; new version bump (tag: apod-2023-12-29, upstream/master, master) [Bill Kendrick]
*   257d5683 2023-12-29 | Merge branch 'master' of https://github.com/FujiNetWIFI/fujinet-apps [Bill Kendrick]

Notice how our commit id has changed! It used to be 248f743d but has changed to a94ba9cc. This is because any rebase or amend to a git commit gives it a completely new hash id, as the hash is based on the history, which happens when you rebase your changes, hence the commit id changes.

We are now ready to retest our changes (after all, your changes are now on top of new code, so should be retested!) before pushing them to our fork, and create a PR with git push.

upstream.sh

Here's the script, I use it on MSYS2 and Ubuntu. Untested on Mac.

#!/bin/bash
# Usage: upstream.sh [target-branch-if-not-master-or-main]
#
# Fetch upstream changes, merge them in with ff-only to our main/master
# branch, then rebase our current work on top of that.
# Specify the "main" branch you want to rebase against as a parameter
# if it is NOT one of main/master.

UB_SPECIFIED="false"
if [ $# -eq 1 ] ; then
  USER_BRANCH="$1"
  UB_SPECIFIED="true"
fi

if [ -n "$(git status --short)" ] ; then
  echo "Your working tree is not clean. Aborting"
  exit 1
fi

git remote | grep -s 'upstream' > /dev/null
if [ $? -ne 0 ] ; then
  echo "No upstream remote found."
  echo "Please use 'git remote add upstream <URL to remote git repo>' to use this"
  exit 1
fi

# main or master branch name
if [ "$UB_SPECIFIED" == "true" ] ; then
  BR_MATCH="(\b$USER_BRANCH\b|\bmaster\b|\bmain\b)"
else
  BR_MATCH="(\bmaster\b|\bmain\b)"
fi
M_BRANCH=$(git branch -v --no-color | sed 's#^\*# #' | awk '{print $1}' | grep -E "$BR_MATCH" | head -1)

if [ -z "$M_BRANCH" ] ; then
  echo "Could not find branch to merge against. Used patter: $BR_MATCH"
  exit 1
fi

# jumps to master/main if needed, fetches, merges and pushes latest upstream/master changes to our
# repo, then rebases our branch on top of master/main

current_branch=$(git rev-parse --abbrev-ref HEAD)
if [ "$current_branch" != "${M_BRANCH}" ]; then
  echo "Changing from branch ${current_branch} to ${M_BRANCH}"
  git checkout ${M_BRANCH}
fi

echo "Fetching upstream changes"
git fetch upstream ${M_BRANCH}
echo "Merging changes (fast forward only)"
git merge upstream/${M_BRANCH} --ff-only
if [ $? -ne 0 ] ; then
  echo "ERROR performing ff-only merge. Rebasing against upstream"
  git rebase upstream/${M_BRANCH}
  if [ $? -ne 0 ] ; then
    echo "ERROR rebasing to upstream. Your changes conflict with upstream changes."
    git rebase --abort
    exit 1
  fi
fi

if [ "$current_branch" != "${M_BRANCH}" ]; then
  echo "Resetting back to previous branch ${current_branch}"
  git checkout $current_branch
  echo "Rebasing local changes with external. You may need to fix merge conflicts"
  git rebase ${M_BRANCH}
fi