Recently I’ve been using and recommending the GitHub Desktop client for non-programmers that need to interact with a Git repository. However, I ran into some interesting issues with the end result when multiple users are participating. This blog post takes a methodical walk through the common actions and how they behave when ‘merge’ or ‘rebase’ is the default.
The process I’ll follow will be to have two separate projects (test-merge and test-rebase), each being edited by two users (Mike and Sonja). The actions that will be tested are:
- Add a new file
- Change an independent file, commit, pull, push
- Change an independent file, pull, commit, push
- Change the same file, commit, pull, push
- Change the same file, pull, commit, push
- Delete a file
Add a New File
Initially we’ll start with Mike adding a file, committing and pushing to GitHub. Then Sonja will clone the repo, add a second file, commit and push. Mike will then pull.
As expected, both sides end up with both files, and the repo looks good.
Change an independent file, commit, pull, push
In this case, Mike will change his file, commit it to his local repository. Then Sonja will change her file, commit it to her local repository, push the changes. Finally, Mike will pull the changes in, and push his changes.
Interestingly, this generated a special merge commit. Because Mike attempted to push his changes after Sonja, GitHub Desktop prompted with a message indicating that Mike’s local repository was behind and needed to be updated.
He then needed to click the ‘Pull’ button to bring Sonja’s changes into his local repository. A new change was automatically created that merged Sonja’s ‘newer’ changes into his repository. Mike could then click ‘Push’ to send the changes to GitHub. Note that there are 2 changes (Mike’s original change and the merged change from Sonja) that Mike is sending. That’s reflected in the Graph that shows Sonja’s changes (blue line) being merged together.
Now, if instead, we do the process with a rebase (git config pull.rebase true
), then the experience is almost identical. The only perceived difference is that Mike sees a “Pull with rebase” button instead of a “Pull” button. Once Mike does the Pull and Push, the history looks much cleaner, since Mike’s latest changes are rebased on top of Sonja’s changes.
Change independent file, pull, commit, push
This time, the Mike user will make the change, but won’t commit before pulling Sonja’s changes.
In the case of the merge, everything worked as expected. No special commits were made.
In the case of the rebase, when Mike went to pull in Sonja’s changes, an error was displayed:
Mike was able to click ‘Stash changes and continue’, the pull then succeeded. Mike had to then click ‘Stash’ and ‘Restore’ to pull the changes. As expected, the graph looks fine:
While this provides a better Git history, it is much more complicated for the user. Fortunately, there is another Git option that can fix it. Setting git config rebase.autoStash true
enables the Stash process transparently. The end result, is Mike just clicks Pull, Commit, Push normally, and doesn’t notice his changes were Stashed and Unstashed before and after the Pull.
However, one important point is that the stashing can cause difficult merge conflicts depending on the type of file and the type of changes.
Change the same file, commit, pull, push
In this scenario, Mike will change his file, commit, then Sonja will change Mike’s file, commit and push, and finally, Mike will pull Sonja’s changes and push. To make the conflict easier, Sonja’s change will be 100 lines away from Mike’s.
In the case of the Merge, we again see the dirty error, then a pull, and a push. Since Sonja’s change was far enough way, there was no merge conflict. The end graph again shows the merge update.
In the case of the Rebase, the same error, Pull with rebase, and push, worked, and the graph is clean:
Change the same file, pull, commit, push
And finally, Mike will make a change, Sonja will make a change, Sonja will push, Mike will pull, then Mike will commit and push.
In the case of the Merge, we again see the dirty error, then a pull, with a stash.
And with the rebase, we get the same messages, and a clean graph.
Summary
For certain cases, most notably when there is only one branch, and multiple people are working on the same branch, and there is not alot of changes of the same file by multiple people, I would recommend using a rebase with autostash. This can be set into your Git repo by typing (if starting in GitHub Desktop, click Repository -> Open in Command Prompt
to get a shell):
git config pull.rebase true
git config rebase.autoStash true