This is one of those posts that people sometimes do in order to document something. In this case, this is me documenting something that I needed to do at work today.
What do we want? We want to publish something we have been working on to GitHub. But we want the GitHub history to be clean and tidy, while keeping the private history. We also want to be able to continue developing, as usual, with our private repo, but push new features from time to time to GitHub without exposing the whole private history. So this is what we did:
The following workflow seems to provide what is necessary to:
- Develop in a private repo (stash/bitbucket) with the usual perks:
- Feature branches
- PR / Code reviews
- Publish releases to a public repo in GitHub, with the following constraints:
- Its own, clean public history.
- History different from the private repo’s history, but not totally independent from it.
- The release process to GitHub should be as simple as possible
This workflow is based on the workflow described in this article: Our Git Workflow: Private Development, Public Releases
Add GitHub remote
This step can be done anytime before pushing to GitHub.
$: git remote add github firstname.lastname@example.org:username/repo.git
Initial commit to master
Right after the project is created from scratch, create an initial commit. This should happen immediately after opening a new project in Android Studio / Xcode / IntelliJ / AppCode…
This branch will be the private master, and can be pushed to bitbucket/stash
$: git add . $: git commit -m "initial commit" $: git push -u origin master
Create a release branch
Right after creating the initial commit, and pushing to master, create a release branch: release/release. This branch will contain the full history.
$: git checkout -b release/release $: git push -u origin release/release
Create the branch that will be pushed to GitHub
Branch out from release/release, before adding any other commit:
$: git checkout -b release/github-master $: git push -u origin release/github-master
At this point, there should be three branches, containing the same commit.
Work on a feature
Starting from master, checkout a new feature branch, and publish it to stash/bitbucket
$: git checkout master $: git checkout -b dev/my-awesome-feature $: git push -u origin dev/my-awesome-feature
After development is completed, and all work in this branch is pushed to the repo, the branch will be merged into master and removed from the remote. Make sure local master is up to date with the remote master.
Prepare a release
Local master is ready for release. So now it is time to checkout the release branch, merge master into it, and do whatever preparations are needed for the release (bump version numbers, update README or CHANGELOG files, and whatever else is necessary):
$: git checkout release/release $: git merge master $: pico README $: git add . $: git commit -m "Update README, CHANGELOG and bump version to 1.0.1"
Now, the release branch can be merged into master. But the important step in the following:
Prepare the public branch
github-master is the branch where we don’t want to see every single commit in the private history. So, checkout the github-master branch, and merge the release branch into it, making sure it is merged as squashed. That squashes all the diff in one single commit, and leaves the changes stashed in the github-master branch, waiting to be committed in one single commit:
$: git checkout release/github-master $: git merge --squash release/release $: git commit -m "New feature: Whatnot" $: git tag 1.0.1 -m "Release: new feature - whatnot" $: git push github HEAD:master
This will make the github-master branch history clean, with a single commit for each squashed merge.