I’m teaching a Startup Systems Design and Engineering class at Cornell Tech this year. As a first homework assignment we’ve asked students to fork our github repository and add a page with their own info and maybe a photo, linked from the README. A good pull request is done off a feature branch and includes 3 files: the README change, a personal page and a picture named the same way as the personal page file. Here’s a good one from one of our TAs, minus the feature branch.

Since most students use git for the very first time they often make some kind of mess. Everyone successfully forks the repository, clones it locally, makes changes (usually on master), commits and pushes, but that’s where the issues begin. They try to update from the upstream repository, squash commits, rebase things and make a huge mess. This is normal, git is very powerful and can be very confusing. Eventually it becomes muscle memory and you can only really get there by doing it yourself.

After helping a handful of students I decided to write this up. Here’s a quick tutorial on getting out of a git mess.

Make a Clean-ish Slate

The first goal is to make both the local computer’s and our Github fork’s master branches look like the upstream, the repository that was forked, master branch, without losing changes already made.

If there’re any changes here, git stash them away. If you were in the middle of a merge, run git merge --abort. If you were in the middle of a rebase, run git rebase --abort. Run git status again and make sure everything is clean.

You may get a “Your branch and ‘origin/master’ have diverged, and have 2 and 8 different commits each, respectively.” error message, which is normal, because your cloned local copy is not the same as your Github fork, they have diverged.

Now master is in sync between upstream’s master, your fork’s master on Github and your computer’s master, we’re back to a state after we’ve forked the repository, and our changes are on that tmp branch.

Apply Changes to a New Branch

We saved our work to tmp. Create a new branch called students-list and merge all changes from tmp to it, minus the history with git merge --squash. That flag applies the changes but doesn’t replay the commits.

Use git status to see changes. They aren’t committed, it’s as if you just made them with a text editor.

(students-list)$ git status
On branch students-list
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: ...

Examine Changes, Commit and Push

Add any changed files with git add, commit changes with git commit and push the branch to Github.

(students-list)$ git push origin student-list

Make a pull request from the student-list branch to the upstream’s master branch on Github. You’re all set.

Need More Changes?

If you need to make more small changes to the student-list branch, edit the files, git add them and commit using git commit --amend, which updates the last commit instead of adding a new one, then git push origin student-list -f to push the changes to Github. This way there’s only one commit in the pull request, no need to squash.

Rebase

There’s going to be other changes on the upstream repository by other students. Update your fork.

When a conflict is shown during the rebase, fix it by editing the file in conflict with a text editor, then continue the rebase with git rebase --continue.

Push the branch to Github.

git push origin student-list -f

No need to update an existing pull request from the student-list branch, it will be automatically updated with the new changes!

Cleanup

After the pull request has been accepted upstream (ie. merged), checkout the master branch, git pull upstream master, then cleanup local branches.

(master)$ git branch -D tmp
(master)$ git branch -d student-list

The -D force-deletes unmerged branches with changes and -d deletes all merged branches.

You may get a “error: unable to delete ‘branch-name’: remote ref does not exist” when trying to delete a branch. Prune any branches that were deleted remotely.

(master)$ git remote prune origin

You can see the remote branch list with git branch -r.

(master)$ git branch -r
origin/student-list

Delete the merged remote branches.

(master)$ git push origin :student-list

Conclusion

The biggest issue was that you started without a topic or feature branch, student-list in our example. That makes it hard to have a local baseline (master) to rebase or update against. Always keep all the master branches in sync, and use feature branches for development.

Was this blog helpful? Help me raise $2,620 to run the NYC TCS Marathon 2018 with TeamForKids.