9

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.

Parker Hoyes
  • 2,118
  • 1
  • 23
  • 38
  • So, Is it enough to have a **master** or you actually need a forged initial commit with a past date? – JSelser Jul 11 '15 at 02:58
  • @JSelser Having the initial commit with the correct date would be nice but as long as the repo functions effectively, with the master branch representing the production-ready state of the project, then it solves my problem. – Parker Hoyes Jul 11 '15 at 03:01
  • 1
    Does the `develop` branch reflect the correct production-ready history? Just `git branch master develop`. There's no such thing as an inherent branch base in git, there's only where two histories diverged. – jthill Jul 11 '15 at 03:28
  • @jthill No, the `develop` branch represents the development state of the project as it should. Since there's never been a release and the `master` branch wasn't created from the beginning, there is no branch representing the production-ready state of the project. – Parker Hoyes Jul 11 '15 at 03:30
  • If the develop branch doesn't already reflect the history you want, rebasing it won't help. Draw the commit graph you have, remember that each commit is a full snapshot of the project state, and add any new commits you want to have that reflect the production snapshots. It's the graph that matters, not what labels you hang on the tips. How are you going to produce your first production-ready-state commit? – jthill Jul 11 '15 at 05:37
  • @jthill The repo should follow the Gitflow Workflow, and in order to do this an initial commit must be made to a new orphan branch `master`, onto which the `develop` branch will be rebased. See my answer below. – Parker Hoyes Jul 11 '15 at 05:58
  • Possibly related: https://stackoverflow.com/questions/21843130/can-i-orphan-an-existing-branch – Gabriel Devillers Dec 05 '19 at 20:09

3 Answers3

8

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.

Community
  • 1
  • 1
Parker Hoyes
  • 2,118
  • 1
  • 23
  • 38
3

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.

Community
  • 1
  • 1
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • This would be a viable solution, except in my case the initial commit on `develop` should not be considered a production-ready state of the project. See my updated question. – Parker Hoyes Jul 11 '15 at 03:15
  • @ParkerHoyes then a rebase is indeed mandatory: I have amended the answer accordingly. – VonC Jul 11 '15 at 03:19
  • This method did point the initial commit on `develop` to the initial commit on `master` correctly, however after the rebase the feature branches are no longer pointing to the correct commits. – Parker Hoyes Jul 11 '15 at 03:27
  • Your updated solution still ended up with the same result. The feature branches point to the old `develop` commits, instead of the rebased ones. – Parker Hoyes Jul 11 '15 at 03:43
  • @ParkerHoyes Darn it, I knew my original answer was much simpler ;) I have edited my answer to rebase all the feature branches as well. – VonC Jul 11 '15 at 03:57
  • Have you tested your updated answer? After running it myself the result was a branch `develop` and `master` with only the initial commit, and the feature branches still pointing to the old `develop` branch. – Parker Hoyes Jul 11 '15 at 04:31
  • @ParkerHoyes no I did not. Can you try last part with just one of the feature branches: `git rebase --committer-date-is-author-date --preserve-merges --onto master tmp^ aFeatureBranch`, to see if that feature branch is properly relocated? – VonC Jul 11 '15 at 04:36
  • I came up with a simpler solution that fixed my problem. See my answer. This is generally only a fix that needs to be applied one-time, so doing the job manually shouldn't be much of an issue. – Parker Hoyes Jul 11 '15 at 05:44
0

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).

Joseph K. Strauss
  • 4,683
  • 1
  • 23
  • 40