In some occasion you canât work with only the master main branch.
For example when working with open source, the upstream repository is not owned by you.
So in order to add your contribution youâd likely need to fork it and make a pull request.
As of the release of this article, most online git like GitHub renamed
masterintomainfor the default branch.
Practice environment
In order to practice all that without making pointless pull request to actual project. You can either do it on one your own repository, or use this demo Upstream Organization on GitHub.
Make sure your GitHub account is set up. Or follow âGet started With GitHubâ for a quick ramp up.
For the example, you can fork this Upstream Repository for this example.
You should now have <your username>/UpstreamRepo in your GitHub repositories.
Then clone your fork locally:
git clone git@github.com:<your username>/UpstreamRepo.git
Letâs review the git workflow and try to make some contributions.
Workflow setup
Set up your fork
Letâs configure the upstream, and add some rule, so you donât try to push to upstream by mistake:
git remote add upstream "git@github.com:UpstreamOrg/UpstreamRepo.git"
git remote set-url --push upstream nope
git remote -v
Not that you can set any url as upstream, here by putting nope it will just not work.
You should see something like that with your username instead of mine:
origin git@github.com:sylhare/UpstreamRepo.git (fetch)
origin git@github.com:sylhare/UpstreamRepo.git (push)
upstream git@github.com:UpstreamOrg/UpstreamRepo.git (fetch)
upstream nope (push)
%%{init: { 'theme': 'base', 'gitGraph': {'showBranches': true, 'showCommitLabel':false}} }%%
gitGraph
commit
commit tag: "fork point"
branch upstream
commit
commit
commit tag: "upstream ahead"
You should have origin as the forked repository and upstream as the upstream repository.
If you have cloned the wrong one you can always update the remote branches using:
git remote set-url origin git@github.com:<your username>/UpstreamRepo.git
Sync your fork with upstream/main
When you fork, you copy the upstream at a point in time. So since you create feature branch out of your fork, you need to keep your fork in sync. To do that, you should make no commits should be directly made in origin/main.
To your sync fork origin/main with upstream/main use:
git checkout main
git fetch upstream
git merge upstream/main # Or git rebase upstream/main
git push origin
%%{init: { 'theme': 'base', 'gitGraph': {'showBranches': true, 'showCommitLabel':false}} }%%
gitGraph
commit
commit
branch upstream
commit
commit tag: "fetch upstream"
commit
checkout main
merge upstream tag: "rebase"
This way you can keep your main branch as a mirror of upstreamâs main. If youâve deleted the branch locally, you can use:
git checkout --track origin/main
That will track locally the remote origin/main branch.
Create a new feature branch
Once your fork is up-to-date, you can create a feature branch using:
# Create and go on branch
git checkout -b feature-branch
# To push branch remotely
git push -u origin feature-branch
%%{init: { 'theme': 'base', 'gitGraph': {'showBranches': true, 'showCommitLabel':false}} }%%
gitGraph
commit
commit
branch upstream
commit
commit
commit
checkout main
merge upstream tag: "sync fork"
branch feature-1
commit
commit
checkout upstream
merge feature-1 tag: "PR merged"
checkout main
merge upstream tag: "sync after PR"
Donât forget to sync your forkâs origin with upstream/main once your PR gets merged, so that you wonât be creating your next feature branch on an old version of the code.
Work on someone elseâs fork
Same as working with upstream you can add a remote to your Colleague branch:
git remote add colleague "git@github.com:Colleague/UpstreamRepo.git"
Then you can pull and checkout on the branch that you want to work on.
When pushing, it will update the branch your colleagueâs fork Colleague/UpstreamRepo.git using:
git push colleague
%%{init: { 'theme': 'base', 'gitGraph': {'showBranches': true, 'showCommitLabel':false}} }%%
gitGraph
commit
commit
branch upstream
commit
branch colleague-feature
commit
checkout upstream
commit
checkout main
merge upstream tag: "sync"
branch feature-1
commit
commit tag: "push origin"
checkout upstream
merge feature-1 tag: "your PR merged"
checkout colleague-feature
commit tag: "push colleague"
checkout upstream
merge colleague-feature tag: "colleague PR merged"
When pushing on âcolleagueâ, you are not pushing to your fork, but to your colleagueâs branch on his fork.
Rebase your feature branch from upstream/main
Thatâs usually before merging your feature branch, you need to add all the changes from upstream/main. To do that go on your feature branch and do:
git fetch upstream
git rebase upstream/main
git push -f origin
What it does is to fetch all the commits from upstream/main and add them to your feature branch. That may produce merge conflicts that you will need to solve. Then you can force push to remote once your branch is up to date.
%%{init: { 'theme': 'base', 'gitGraph': {'showBranches': true, 'showCommitLabel':false}} }%%
gitGraph
commit
commit
branch upstream
commit
commit
checkout main
merge upstream tag: "sync"
branch feature-3
commit
checkout upstream
commit tag: "fetch"
commit
checkout feature-3
merge upstream tag: "rebase"
commit
checkout upstream
merge feature-3 tag: "PR merged"
You will see this error if you donât force push with -f to rewrite the history of your fork with the changes that were merged into main.
error: failed to push some refs to 'git@github.com:user/repo.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
Branches allow asynchronous changes by default, which is why you can have changes made behind your own commits.
Suppress your feature-branch
When youâre done with a feature-branch or when youâve messed up locally, you can always delete it with:
# -D for force delete, otherwise -d
git branch -D feature-branch
Automation đ€
Git Aliases
So now that we have seen the workflow, letâs add some alias in your ~/.gitconfig to ease it up:
[alias]
sync = !git stash && git checkout main && git fetch upstream && git rebase upstream/main && git push origin
new = !git checkout -b $1 && git push -u origin $1 && :
fur = !git fetch upstream && git rebase upstream/main
pull = pull --rebase
You got it, it should make your life easier.
Bash script
This one is mainly to automate the creation of an upstream repositoryâs fork from GitHub. Basically it needs two inputs:
- Your fork repositoryâs SSH address
- The Upstream organisation name
Here is the clone script that should be in your ~/.zshenv:
clone() {
upstream=$1
repo=$(echo "$2" | awk -F"[/.]" '{print $3}')
echo "1. Cloning repo $repo at $(pwd)/$repo"
git clone "$repo" && cd "$repo" || return 1
echo "2. Setting up upstream $upstream and disallow push"
git remote add upstream "git@github.com:$upstream/$repo.git"
git remote set-url --push upstream nope
git remote -v
echo "3. Checkout to another branch"
git checkout -b dev
}
Using it while replacing with <your username> should be:
clone UpstreamOrg git@github.com:<your username>/UpstreamRepo.git
The auto checkout to another branch in the end is to avoid any mistaken commit to main, as previously stated we want to keep it clean to sync up with upstream.