Open edX Git Workflows#
The Open edX project is available on GitHub, which uses the Git system for version control.
When contributing to a new repo, contributors should use a personal fork, follow OEP-51: Conventional Commits, squash changes into logical commits, and rebase branches before requesting reviews. This page will describe how to do all of these things!
Once you’ve set up your flow, follow the instructions on Landing Your Work.
Using A Personal Fork#
Personal forks are a standard open source tool for making pull requests against repos where you don’t have write access. It’s a little more complicated than working directly in the repo, but it’s not difficult.
There are many Git tools, and styles of working. These instructions focus on the
Git command line tool (git
). There are other ways to accomplish the same workflow,
including more GitHub GUI-centered, or the gh GitHub command-line tool. We
welcome improvements to this page.
Terminology#
Fork: on GitHub, a fork is a copy of a repo in your own account so that you have write permissions to push commits and branches to it. This is different than the broader open source term of forking a project because of a disagreement about direction. We are talking about the gentle GitHub term, not the aggressive governance term.
Upstream: the repo you are ultimately contributing to. In our world, this is
likely in the openedx
GitHub organization.
Remote: a server copy of a repo. A local repo on your machine can have a number
of different remotes, and pull from them separately. Each remote has a name and
a URL. The default remote is called “origin”. You can see your remotes’ names
and URLs with git remote -v
.
The Ideal State#
You will have a local repo with two remotes: “origin” will point to your fork in your account, and “upstream” will point to the repo in the openedx org. Very briefly, the normal flow will be:
Pull from the upstream into your fork to get the latest code.
Make a feature branch in your repo.
Make your changes, and commit them.
Push your branch to your fork (origin).
Make a pull request against upstream.
Once the review is complete, the owning team will approve and merge the pull request.
Creating a fork#
Creating a fork is easy:
Visit the repo on GitHub.
In the upper-right corner is a Fork button. Click it:
On the next screen, you’ll be asked to choose which GitHub account the repository should be forked to — this is shown as the Owner field. Choose your own personal account (the default choice):
If you have permissions to fork into your company’s organization, you should check carefully with your process owners to see if you should.
The downsides to forking into your organization:
Now there will appear to be two sources of truth:
openedx/repo
andmyorg/repo
. Developers will have to be careful about which to clone, and which to merge into.There’s now a shared task of keeping the two repos in sync.
Getting the fork locally#
How you get the fork locally depends on whether you already have a local repo copy that you want to keep. If you have a local copy but don’t need it, you can delete the repo directory and use the “I do not have a local copy” section below.
I do not have a local copy#
Clone the repo locally:
git clone https://github.com/USER/REPO.git
Change to the new repo directory:
cd REPO
Add a new remote for upstream:
git remote add upstream https://github.com/openedx/REPO.git
I have a local copy I want to keep#
Warning
The rename will change the remote of all your branches to upstream, which is not what you want. So, it is probably best to complete your work, remove the old copy, and then follow the steps in “I do not have a local copy.”
Rename the existing remote:
Use
git remote -v
to check that the openedx repo is named “origin”Rename the remote to “upstream”:
git remote rename origin upstream
.
Add a new remote for your fork:
git remote add origin https://github.com/USER/REPO.git
Keeping your fork up to date#
You now have two remotes: your fork called origin
and the openedx core repo
called upstream
. Before beginning work on a change, you will want to get the
latest code from upstream into your fork. Keep in mind that some repos use a
master
branch, others use main
.
One simple way to keep your fork up to date is to visit the GitHub page for your
fork (https://github.com/MYUSERNAME/REPO
) and click the “Sync Fork” button.
Then locally, you can pull down the changes on the master
or main
branch.
To do this via the command line follow these instruction. If you are in a main
repo, replace master
with main
:
Switch the current branch to master:
git switch master
.Pull down the changes from upstream and update your master branch:
git pull upstream master --ff-only
If everything has been done right, this will be a fast-forward merge, with no explicit merge commit. You shouldn’t have changes on your master, but if you do, the
git pull
command will fail with “fatal: Not possible to fast-forward, aborting.” If this happens, you will need to fix your master branch before continuing. One way is to make a branch off your master branch with your changes, then switch back to the master branch and remove your commits with an interactive rebase or by usinggit reset --hard <commit>
.Push the changes to your fork to keep it up to date:
git push
.
Now your master branch is in sync with upstream, both locally and in your fork on GitHub.
Making a pull request#
Making a pull request is very similar to the simple one-remote workflow:
Create a branch locally:
git switch -c user/description
.Make your changes and commit them.
Push your branch to your fork:
git push -u origin @
(@
is shorthand forHEAD
, i.e., your current branch).Make a pull request on GitHub. The base repository should automatically choose the upstream repo.
Review and work on the pull request as usual. You can push new commits to your branch as usual.
Ask the owning team to approve and merge your pull request.
Once your code has been merged, the steps in “Keeping your fork up to date” will get you ready for the next iteration of work. You can also use those steps if you need to rebase your pull request to base it on the latest changes on master.
Squashing Changes#
When merging commits to upstream master
and main
branches, we prefer
tidiness over historical accuracy. In other words, we would prefer that you
merge in a small number of well-documented, individually-coherent commits rather
than a large number of commits that detail all your intermediate development
states and bugfixes. We achieve this via squashing, which means combining
multiple git commits into a single commit with the same cumulative changeset.
As a PR author, you can help us keep the upstream git history tidy in two ways:
Before submitting your PR for review, perform an interactive rebase, and squash away any commits which will not be useful to other developers.
During your PR review, as you edit your code in response to feedback, name your new commits using the special git squash! or fixup! prefixes. This will indicate to the repository’s core contributors that these commits should be squashed away as part of the final merge to
main
ormaster
. It will also allow the core contributors to auto-squash your commits more easily.
As a core contributor, when you are ready to merge a PR, take a look at the commit log. How many commits are there to merge excluding ones that need to be squashed away? If there is only one non-squashable commit, then you can simply press the “Squash and Merge” button, which will open a visual interface allowing you to squash all the commits into one (but please edit the message to something useful rather than using the default one). If there are multiple non-squashable commits, then you can pull the branch down and perform an interactive rebase to revise the commit list. You’ll then need to force-push and select “Rebase and Merge” on GitHub.
See also the git-scm book section on squashing changes.
Rebasing Your Branch#
It is best practice to rebase your branch atop a repo’s main
or master
branches before requesting a review. This ensures your branch is up to date with
the repo’s latest changes, preventing conflicts.
Heads Up!
It is strongly recommended to squash your changes before rebasing. Otherwise, you may end up having to resolve similar conflicts over and over.
Rebasing can be complicated, but the happy-path is as follows:
Switch to the
master
ormain
branch of the repo, and sync upstream changes.Switch back to your working branch.
Run the rebase command:
git rebase master
orgit rebase main
If there are no conflicts, you will see your branch rebased successfully. You’ll now need to force-push your changes:
git push -f
orgit push --force-with-lease
.
If there are conflicts, the rebase will stop on the conflicting commit and wait
for you to resolve the conflict.
Once the conflict is resolved, you can continue the rebase with git rebase
--continue
.
See the git-scm book section on rebasing for more technical details on rebasing.
Further Git Resources#
The Open edX community has found the following resources helpful in their journey to becoming Git experts: