I'm working with a repository that in theory should be following the Gitflow Workflow (see A successful git branching model by Vincent Driessen). However, the initial commit on the repository was made on the develop
branch and there is no master
branch to be seen. It's nearing release time and I need to create a master
branch that reflects the production-ready state of the project which should've been there from the start. Keep in mind that the develop
branch has multiple feature branches coming off of it. The repository is entirely local and hasn't been pushed.
My idea was to create an orphan branch master
and rebase the develop
branch onto it, but I don't know how I'd go about doing that.
So, how can I create the master
branch as if it was created from the start?
Update: In my case, the first commit on develop
is not a commit that should be considered suitable for production, so using this as the initial master
commit would be unwise. The reason that the project is in this state is because it was originally not using a VCS when it was decided to use Git.
After some fiddling, this is what I came up with. This is a simpler, manual approach to VonC's answer.
Rebasing an Entire Development Branch
Let's assume you have a branch develop
which contains the initial commit of your repository, and you'd like to rewrite history such that a master
branch contains the new initial commit instead.
First off, if the inital commit on your develop
branch is suitable as the initial commit on the new master
branch, then you can simply create the master
branch there and you're done:
$ git branch master <sha1-of-initial-commit-on-develop>
If you don't have that luxury, then you'll need to create a new empty commit that will serve as the initial commit of master
.
# Create the new master branch
$ git checkout --orphan master
# Clear the working directory (we want the initial commit to be empty)
$ git rm -rf .
# Create the initial commit on master
$ git commit --allow-empty -m "Initial commit"
# Rebase the entire develop branch onto the new master branch
$ git rebase --onto master --root develop
If there were any branches coming off of the develop
branch, they would've been "majorly messed up". This is because those branches (we'll call them topic branches) are still pointing to the old develop
branch before it was rebased. If you had no branches coming off of the develop
branch, then you're done.
Each topic branch is going to have to be rebased onto the new develop
branch. To do this, we're going to follow the steps outlined in another question (Git: How to rebase to a specific commit?). For each topic branch, follow these steps.
Replace <common-ancestor>
with the sha1 of the commit on the newly created develop
branch where the topic branch should branch off of.
$ git branch temp <common-ancestor>
$ git checkout <topic-branch>
$ git rebase temp
$ git branch -d temp
And that's it! Keep in mind that you should not rebase on a branch that you're collaborating on with someone else.
Ideally, you would need to rewrite the full history of dev
by adding a commit at the start:
# remember the first dev commit (before rebase)
git branch tmp $(git rev-list --max-parents=0 HEAD)
# first you need a new empty branch
git checkout --orphan master
git rm -rf .
# then you apply the same steps
git commit --allow-empty -m 'root commit master'
git rebase --preserve-merges --onto master --root dev
However, as illustrated in "Rebasing a branch including all its children", that would leave all the feature
branches pointing at their old dev
origin (before rebase).
git branch --contains tmp | \
xargs -n 1 \
git rebase --committer-date-is-author-date --preserve-merges --onto master tmp^
That is: any branch that was accessible from the old dev
first commit (tmp
) needs to be rebased on master
: any common commits already rebased on master won't be repeated. That will recreate the commits from the feature
branches, from the new (rebased) dev
branch.
Original answer:
You could simply have created the master
branch from the first commit of the dev
branch.
git branch $(git rev-list --max-parents=0 HEAD) master
(See "How to reference the initial commit?")
That means the first commit done on dev
is also considered part of the master
, which isn't entirely accurate, but easier than to rewrite the entire history of dev
.
From the Git filter-branch documentation.
To set a commit (which typically is at the tip of another history) to be the parent of the current initial commit, in order to paste the other history behind the current history:
git filter-branch --parent-filter 'sed "s/^\$/-p <graft-id>/"' HEAD
(if the parent string is empty - which happens when we are dealing with the initial commit - add graftcommit as a parent). Note that this assumes history with a single root (that is, no merge without common ancestors happened).