8

Now I know most git experts will immediately think of git rebase, but I am using the word "rebase" in the more general sense: I have the following structure of my project:

.
..
.git
tools
lib
src
    .git
build

Both . and ./src directory are evidently git repositories, and have literally a long history and large number of commits each. The repository in . ignores src directory (which is its own repo).

I have just realized that I would instead want to just have one single repo in . tracking everything including the source files, because frankly, the build system is evolving along with the source code, and has become quite extensive.

My problem is that I don't know how to have this repository retain history that is now part of the repository in src. Is it even possible? This is what I meant by 'rebasing' - if changes in ./src/main.c are tracked by ./src/.git across some N commits, then I want to retain these changes and have them be part of the new repository ./.git. Same history, rebased file path.

UPDATE

Subtree merging is something else than what I want, from what I gathered on SO. In short, it does much more than what I need. I merely need the content of the old repo, together will all branches of development and all commits, tags, etc to look as if they were always part of the parent repo. In essense, the only change is paths to files themselves - where before the child repo tracked ./main.c, the new repo will now track ./src/main.c, and since as I have heard, git tracks content, not files, then changing file paths like the above and references to these paths, should be rather trivial, correct?

Armen Michaeli
  • 8,625
  • 8
  • 58
  • 95
  • Ask google for "subtree merging", that's what you want. – Nevik Rehnel Feb 12 '13 at 16:24
  • I have just spent an hour at least, and have concluded that neither subtree merging nor submodules fit me. Subtree merging retains not only history but also the link between the old and the new repos - while my old repo will be retired and discarded. Submodules are out of question because my repos are very tightly coupled. I have found the following answer to be the most fitting for me, but I really want to merge **all** branches, not just master: http://stackoverflow.com/questions/13040958/merge-two-git-repositories-without-breaking-file-history/14470212#14470212 – Armen Michaeli Feb 12 '13 at 18:25

4 Answers4

2

Quick and easy way:

Rename all files in src, so they will start with src/. Add src repo as remote, fetch & merge. Drop old src repo, everything is now in ./.

This will leave you with this action recorded in history.

History rewrite:

To make this merge invisible, you need to use git filter-branch --tree-filter to add src/ prefix in src repository. Then add this as a remote to ./ repository and fetch it (no merge yet). To blend history nicely, you need to reorder commits. Use git log --date-order master src/master to retrieve these commits in correct order and cherry-pick them:

git checkout -b new-master your-first-commit
git log --format='%H' --date-order --reverse master src/master | xargs git cherry-pick

This basically does merge sort on your commits and lines them up to linear history.

This is will not preserve your merges, so dont do it unless you have flat history. In that case use only filter-branch and then do normal merge.

Josef Kufner
  • 2,851
  • 22
  • 28
  • I am not sure about picking cherries - I want to retain **entire** history, no exceptions. I also want to retain **all** branches, not just master. Will your solution work? – Armen Michaeli Feb 12 '13 at 20:47
  • If you have longer and complex history, cherry-pick part is a bad idea, but filter-branch and merge will work fine. But anyway, with a little luck, you can rearrange commits using interactive rebase (-i) after merge. All I wrote is keeping history, but it is always about one branch. You need to handle each branch appropriately to your situation. – Josef Kufner Feb 12 '13 at 23:50
  • Thank you @Josef. So, automated merging of my nested repository into parent repo, retaining all branches, commits, and history, is impossible? I have to babysit through the process, so to speak? I think I am going to read "git from bottom up" and see if I come up with some sort of script that does all of it automatically. What do you think? – Armen Michaeli Feb 13 '13 at 10:08
  • What is `your-first-commit`? – Torsten Bronger Aug 22 '14 at 06:31
  • `your-first-commit` is the first commit in repository, on wich history will be rebuilt. But be careful with that cherry-pick, it can make a lot of mess in wrong/unskilled hands. – Josef Kufner Aug 22 '14 at 10:47
0

No really use if this would work but could be worth a try. Assuming that the src is on a remote server and the parent is on a remote server. You can try the following.

  • git clone url to remote server to clone the the repo with tools, lib, and build
  • git add remote src url to the src remote repo
  • create a tracking branch of the src remote repo and then check it out on the parent repo
  • merge remote tracking branch into you main branch.
  • delete the second remote.
ams
  • 60,316
  • 68
  • 200
  • 288
0

Use a subtree merge.

I would list out the steps, but they are in the answer to this question: How do you merge two git repositories?

Community
  • 1
  • 1
David Culp
  • 5,354
  • 3
  • 24
  • 32
  • What will happen to branches with identical names in both repos? And also, as far as I could gather, merging involves a single branch, while I want all of history of `src` (with *all* branches). Other than that, subtree merge may be what I want. – Armen Michaeli Feb 12 '13 at 17:17
  • I have updated my question, and I am afraid subtree merge is a bit too much for me, even though I admit it is mostly out of fear and the overwhelming amount of different other strategies I have discovered, and evidence that it (the subtree merge) won't preserve history *accurately*. – Armen Michaeli Feb 12 '13 at 18:28
0

I needed to do the same thing and this worked for me:

From the parent repo,

git remote add -f subrepo git@github.com:sub/repo.git
git merge -s ours --no-commit subrepo/master
git read-tree --prefix=subrepo/ -u subrepo/master
git commit -m "Subtree merged"

This is from a GitHub post that also has more detail: https://help.github.com/articles/about-git-subtree-merges/

lcyh
  • 84
  • 4