Marlin Github Scripts
Github is a great tool for collaboration. You can cover a lot of ground in a short time, and ideas sometimes come quickly and from many directions at once. Wouldn’t it be great if the pace of deployment could be as fast as you can conceive of new ideas? While we may not be able to turn thought directly into code, we can at least provide some tools to smooth the process.
These scripts aim to improve, most especially, the process of managing, patching, and updating active Pull Requests. They don’t rely on any special privileges, but encourage the use of the Pull Request process instead of doing direct commits.
Theoretically, a Pull Request should have a very short lifespan. It should be created, polished, and completed before anything else threatens to stomp on the same code lines. But in practice, some PRs take longer than others to develop and the Marlin policy has been to keep them open till they’re ready. This opens up conflict and stale code issues. The longer a PR sits the harder it is to integrate and the less applicable its changes will be. A Marlin PR can sit for weeks or months.
By also paying careful attention to conflict resolution, these scripts can help keep a feature or patch branch (i.e., based on bugfix-2.1.x
and forming the basis for a PR) fresh for as long as needed.
Merge vs. Rebase
Working with branches and Pull Requests, you soon find there are some operations you need to do over and over, many of which just can’t be done using the Github Desktop graphical interface. And one thing that becomes clear is that merge commits are highly undesirable on active PRs. If you have a PR that needs conflict resolution Github’s tools work fine to patch it up, but the result is an unsightly new merge commit following your PR commits. Since a PR is a new set of changes yet to be added to the latest code, it’s always best to “rebase” instead of “merge.”
These helper scripts are designed for a workflow that uses git rebase
and git rebase -i
exclusively so Pull Request changes always come after the latest merged commits and are therefore relevant to the code at the time of merge.
Interactive Rebase
During the lifespan of a Pull Request, you’ll commonly want to combine or remove commits. This will be done using an “interactive rebase.” All this means is that git
opens up your editor (vim
, joe
, etc.) and you can change the order of commits, rewrite commit messages, drop commits, combine commits together, etc. When you save and close the editor, git
attempts to make the changes.
This is one place where conflicts might arise, since git
can’t always decide which changes should supersede others.
Resolving Conflicts
Git will tell you if there’s a conflict and insert unresolved blocks beginning with <<<<<
into the code. You must return to the project editor, search for these, resolve the conflicts, and then do:
git add .
git rebase --continue
If the resolution resulted in no changes (e.g., because already merged), then you will continue with:
git add .
git rebase --skip
If things get hairy and you don’t know how to proceed, you can always abort and start over with:
git rebase --abort
Conflict resolution is a bit of art and a bit of science, but not too hard to get the hang of. When massive conflicts arise it may be better to skip git rebase
and use git diff
instead. But in ordinary situations with under ~20 conflicts, it shouldn’t be very hard to resolve them.
When resolving conflicts:
- Usually you want some change from both sources. Consider all differences.
- Look carefully. Did a name change, punctuation get added?
- Confused? Reach beyond the current editor. Open the Github website to keep your original commits in view.
Ultimately, the aim of rebase is to apply the equivalent changes to a newer codebase. So use all means at your disposal. If necessary, you can create a temporary branch, recreate your original changes anew, then update the PR to point at the new commits:
mfnew my_redo_branch ;# Create a new clean branch.
;# Make changes, commit them, and then...
git checkout my_pr_branch ;# Go back to the branch with a PR
git reset --hard my_redo_branch ;# Point the PR branch at the new work
git push -f ;# Now the PR contains shiny new commits
Core Scripts
These powerful scripts come with some preliminary requirements:
- A GNU BASH shell environment with GNU utilies
sed
,awk
,gawk
,sort
, and a few others. - Basic understanding of Git branches, commits, some experience with
git merge
andgit rebase
. - Experience with making a Pull Request.
- A Github account and a fork of Marlin in your Github account.
- A checked-out working copy of your Marlin fork. (Get Github Desktop. Use “Open in Desktop” from your fork’s page.)
(It may also be possible to adapt these scripts for the Github Desktop shell environment in Windows.)
Only run these commands from within your Marlin working copy. That is, you first need to run cd /home/work/Marlin
(for example). If you don’t know what any of this is about, start by reading about BASH online, specifically about the “working directory.” Just don’t cd
out of the working directory and all will be well.
WARNING: While short, these scripts can be dangerous and don’t always fail cleanly. Read them to understand how they work and use them with utmost caution. Remember, you can use git reflog
to recover from almost any disaster, so have fun.
ghtp
Usage: ghtp -[s|h] [remote-list]
Use this script to set the “GitHub Transport Protocol” for your remotes to either SSH or HTTP. This determines how Git (and Github Desktop) will connect to Github. You can set all remotes or specify just those you want to change. You should always use SSH with a public-key, unless behind a firewall.
mfinit
Run this script before any others to prepare your working copy of Marlin to use these helper scripts. All mfinit
does is create a remote named upstream
as an alias to MarlinFirmware
. This is a common and convenient shorthand, and is used within the other scripts.
mfinfo
Run this script to get a line of information about your current working copy, branch, and upstream. When working on Marlin the output will look something like this:
Project | Your Account | Repository | Dev Branch | Working Branch |
MarlinFirmware | joehitgub | Marlin | bugfix-2.1.x | fix_something_june_1 |
When working with this website’s repository, the output will will read:
Project | Your Account | Repository | Dev Branch | Working Branch |
MarlinFirmware | joehitgub | MarlinDocumentation | master | master |
The other mf*
scripts use mfinfo
so they know what repository, fork, and so on are currently in use. If you write any custom scripts, you can also use mfinfo
.
firstpush
- Push the current branch to your fork on Github (
origin
) for the first time. - Set the current branch’s upstream to
origin
. - Open the current branch’s commit log in the browser.
Use firstpush
after mfnew
or whenever you want establish the current branch in your fork. The mfpr
script will run firstpush
if needed. This script doesn’t do anything extra if the branch already exists in your fork.
mfnew
Usage: mfnew [new_branch_name]
- Fetch
upstream
to make sure you have the latest code. - Create a new branch with the given name as a copy of the Dev Branch.
Note that the new branch is not sync’ed to your fork. Run firstpush
if you want to create the remote copy.
Use this command when you want to make a patch or feature starting from the latest Dev Branch.
mfpr
Open the Compare & Pull Request page on Github for the current branch. Be sure to use git push
or git push -f
to update the branch in your fork before submitting the Pull Request. You can always use git push -f
afterward if you forget.
mfqp
Quick patch the current branch.
- Commit all uncommitted changes with the message “patch.”
- Run
mfrb
so you can apply the patch withfixup
and make other changes. - Run
git push -f
to update your fork.
Use mfqp
when you want to make a quick correction to an earlier commit and upload it immediately to origin
. This will also update the open Pull Request, if there is one, and is intended to be used when patching up a Pull Request in preparation to get it merged and closed.
mfrb
Rebase the code on the latest Dev Branch and (optionally) rewrite commits.
- Fetch
upstream
to get the latest changes to the Dev Branch. - Rebase the current branch on the Dev Branch. (Conflicts may arise here.)
- Do an interactive rebase to fixup, squash, drop, rewrite, etc.
For the 2.0.x development tree that last step will run git rebase -i upstream/bugfix-2.1.x
. After using mfrb
to rewrite your commit log you must use git push -f
to update your fork. The -f
flag cannot be omitted.
Use mfrb
whenever an open Pull Request is reporting conflicts (thus preventing the PR from being merged), or anytime you want to refresh a branch or Pull Request. Ideally, all open Pull Request should be periodically rebased so that they can be evaluated in relation to the current codebase. This command makes rebasing much easier to do while requiring much less typing.
Whenever possible, take the opportunity to reduce the number of commit using “squash” or “fixup” (preferred).
NOTE: mfrb
may stop for conflict resolution. If this occurs, don’t panic. Carefully evaluate each instance where <<<<<
appears in the code and make sure to choose the resolution that fits the commit being applied (not necessarily the final code).
mfup
Quickly bring a branch up to date, locally and remote.
- Fetch
upstream
to get the latest changes to the Dev Branch - Rebase the current branch on the Dev Branch. (Conflicts may arise here.)
- Run
git push -f
to update your fork.
NOTE: mfup
may stop for conflict resolution. If this occurs, don’t panic. Carefully evaluate each instance where <<<<<
appears in the code and make sure to choose the resolution that fits the commit being applied (not necessarily the final code).
Utility Scripts
mfadd
Add and fetch a remote.
Usage: mfadd MagoKimbra
Use this command to get a branch from another Github user to work on. After making changes and committing them to a copy of another user’s branch, you can submit a PR to the original branch on their fork. If their branch has an open PR, then your PR’s changes become included in their PR when merged.
mfclean
Check to see if branches can be deleted. Remove merged branches and any branches that have been deleted from your fork. Use with care. May not be suitable for all working styles.
Documentation Scripts
Documentation can be worked on with PRs, but these scripts assume a more direct approach for privileged users. You can still check out the documentation, make changes, and submit PRs. The mfpr
, mfnew
, mfqp
, and mfup
scripts are all compatible with the MarlinDocumentation
repository as well.
mfdoc
Use Jekyll to compile the Marlin documentation website and display the local copy in the browser using the small integrated webserver. This command only runs in the MarlinDocumentation
repository with the master
branch checked out.
mfpub
Publish the current documentation website. This command only runs in the MarlinDocumentation
repository with the master
branch checked out. Only committed changes will be published. Uncommitted changes will be stashed and popped, so you can continue to work on them.
- Check out
master
and force-push changes toorigin
andupstream
. - Build the full Marlin documentation website to a temporary folder.
- Check out
gh-pages
and copy the built website into it. - Add and commit changes, then push the
gh-pages
branch.