How to move some parts of a project into Git branches without losing their history?

Viewed 53

I started my main project with CVS in 2006, I moved to Subversion in 2008 and I moved to Git in 2021 but at the very beginning, I was a newbie in version control and my repository is messy, several distinct implementations of my game.

I would like to move them into separate branches but I don't want to lose the history between branches, i.e I would like to be able to find in the log that some files were initially in the "master" branch but were moved into the "pre-alpha" branch. How can I achieve that? Should I create a branch, switch to it, remove the files that have nothing to do in it, go back to the "master" branch and remove the files already in the new branch? Is it the proper way of using Git for this purpose?

P.S: I did this based on larsks' answer:

git subtree split -b prealpha --prefix trunk
git subtree split -b alpha --prefix alpha
git subtree split -b jme --prefix tuer
git push origin prealpha
git push origin alpha
git push origin jme
git subtree split -b prebeta --prefix pre_beta
git branch -m master oldmaster
git branch -m prebeta master
git push origin oldmaster

Only this last line doesn't work:

git push origin master

! [rejected] master -> master (non-fast-forward) error: impossible to push the references to 'https://gouessej@git.code.sf.net/p/tuer/git'

2 Answers

In short...yes.

You should make a new branch off of master, and then make whatever changes are necessary to make each branch (master and the new branch) right.

Note that it can be appropriate to make the branch, and then move a completely different copy of your source into place as your current working copy of that branch. Git will normally do the right thing in terms of the addition and removal of files. Git often won't do so good a job with files that have been renamed or moved. In these cases, a file might be treated as new even though it was really an existing file that was moved or renamed. To fix that, you need to make those changes via git mv on the original contents of the branch to make sure git gets them right.

It looks like your Subversion --> Git migration may have been sub-optimal; your master branch contains a bunch of top level directories that look like they probably correspond to Subversion branches. Your migration to Git should have converted these into Git branches.

I've taken a brief look at the project and I'm having a hard time discerning the relationship between the various branches. I would expect, e.g., that changes to pre_beta would at some point share a common history with the alpha code, but I'm not able to find obvious matches.

We can use git subtree to split those top-level directories into distinct branches. I would do something like:

git subtree split -b alpha --prefix alpha
git subtree split -b pre_beta --prefix pre_beta
git subtree split -b tuer --prefix tuer
git branch -m master old_master
git branch -m pre_beta master

This splits the history for each directory into a separate branch, and then turns the existing pre_beta branch into the master branch (because it seemed to have the most recent changes).

This gets you:

$ git branch
  alpha
* master
  old_master
  tuer
$ git checkout master
$ ls
branding.properties  lib          NOTICE.txt  src
build.xml            LICENSE.txt  README.txt  versioning.properties
$ git checkout alpha
$ ls
about.txt  changes_2.rtf  contents.txt  lib       md3         sound    tools
bean       com            drawer        main      readme.txt  target
build.xml  connection     gpl.txt       manifest  snd         texture
$ git checkout tuer
$ ls
about.txt  changes_2.rtf  contents.txt  jme   md2         snd      tools
bean       com            drawer        lib   obj         sound
build.xml  connection     gpl.txt       main  readme.txt  texture
$ git checkout old_master
$ ls
alpha  pre_beta  trunk  tuer

You can see the result at https://github.com/larsks/tuer-git.

Related