1

I am having a problem with my repository setup: I initialized my repository in the wrong directory and started using it. I already made some branches and merged them again. I would like to keep that history.

How it is now:

wrongRootProjectDir/
|
+-.git
|
+-realProjectDir
   |
   +-<Project files>

How it should look like:

realProjectDir
|
+-.git
|
+-<Project files>

So basically I would like to shift everything one directory up. wrongRootProjectDir also contains only the .git file and the realProjectDir directory.

I found some hints at SO How can I move a directory in a Git repo for all commits? that there could be a way. I run a git clone oldrepo newrepo to get a backup repo and ran

git filter-branch --subdirectory-filter realProjectDir -- --all

on the new repo. It seemed to work, however I have lost my merge and branch structure from the old repository. The filtered repo has the correct paths but is lacking the merge commits. Additionally the old commits are still around, so that the repo looks like this:

(Commit A)
|
(Commit B)
|
(Commit C)
|
(Commit A')
|
(Commit B')
|
(Commit C')

Where A' are the new commits. Is there a problem with my multiple branches and using filter-branches ? So right now I have each commit twice, once with the wrong old path and once with the new one.

Summary

Is there a way of moving a complete directory tree one layer upwards and keeping all branches and merges without generating double commits?

Update I still cannot manage to keep my merge commits and the right branch layout. Everything is flattened into a chain of commits.

Update

Added the history tree before:

Commit history before filter-branch

and after running filter-branch using gitk:

Commit history after filter-branch

The working directory is clean when I run this command. To test things I copied the whole directory tree using simple cp -r to another directory and tested it there. The filter-branch command works in that sense that it moves the subdirectory, but it still flattens the history. As far as I can see it, I have not done anything too complicated, just the normal branching and merging stuff.

Update

I showed the wrong picture for the before state. I tried to transform a non-rewritten repository.

Community
  • 1
  • 1
GorillaPatch
  • 5,007
  • 1
  • 39
  • 56
  • filter-branch isn't what's flattening things, and it doesn't create duplicate commits. Your problem is something else. I've written an answer with some of my best guesses about what's *actually* happening; if that's not enough, you can clearly explain how to reproduce this problem (example original history, exact commands run, final history - including how you're viewing the history) and I can probably help. – Cascabel Feb 12 '12 at 18:24
  • Your before history has filter-branch's backed up refs in it. It's already been rewritten. Are those commits also on your actual branches, which you're going to rewrite? Or are your actual branches elsewhere, with already-flattened history? (Use `gitk --branches` to see just the actual branches.) Your screenshots don't actually demonstrate that those are the commits that are being rewritten into the flat ones. And given that those refs exist, you'd have to pass `-f` to filter-branch to get it to work, too. You didn't mention that either. – Cascabel Feb 12 '12 at 21:17
  • @Jefromi I showed the wrong picture. I just copied out of the already rewritten repo. As you said the commits are backed up. I now replaced the picture with the real before state of my repo. – GorillaPatch Feb 12 '12 at 22:20
  • 1
    Well, I have no idea how to reproduce this given the information you've provided. Either you've managed to discover a really obscure bug, despite all of the internal Git testing and all of the users out there in the wild who've done this tons of times, or there's some missing piece you've left out. Is your repository public? Can you provide a way for others to reproduce this? Can you figure out what's different between what you say works and doesn't work? – Cascabel Feb 13 '12 at 03:53
  • Well no, the repo is not public. I will try later today to reproduce it. Maybe I post a more detailed log output with all commits to get me on the right track. I agree with you that it is far more likely that I am the one to be blamed, not git. However I would like to learn how to get things right. – GorillaPatch Feb 13 '12 at 06:41
  • I played around and tried to reproduce the error with another repo but until now without success – GorillaPatch Feb 13 '12 at 20:52
  • I have the same problem now, it might have something to do with another developer pushing a commit on the old parent while rewriting the history. This caused a merge conflict and after merge/commit all commits seem to be duplicated. – Wiebe Tijsma Aug 28 '14 at 15:48

2 Answers2

1

filter-branch with a subdirectory filter is exactly the tool to do this. This is its entire purpose. If you're having problems, it's because you're doing something wrong that you haven't told us. filter-branch doesn't flatten history. It's possible that you're looking at gitk --all and seeing both the rewritten history and the old history (it'll be marked with original/refs/heads/...) - in that case, note that the two histories aren't connected. It's also possible that you did something to mess things up before the filter-branch. When you're trying again, are you doing it in a fresh clone, starting out from the exact original history you want?

Here's about the simplest possible example demonstrating that this works exactly as it should:

git init
mkdir subdir
touch x subdir/a subdir/b subdir/c
git add subdir/a
git commit -m 'a'
git add subdir/b
git commit -m 'b'
git checkout -b foo HEAD^
git add subdir/c
git commit -m 'c'
git checkout master
git merge foo
git add x
git commit -m 'x'
git filter-branch --subdirectory-filter subdir -- --all
# now inspect e.g. with gitk --all

The original refs are kept around in refs/original/refs/... so that if you screwed up, you can get back to them. If you inspect the results, and decide everything is good, then you can remove them. The method given in the manpage for that is this:

git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d

though in practice you can also just do rm -rf .git/refs/original.

Cascabel
  • 479,068
  • 72
  • 370
  • 318
  • You are right in that respect, that I am seeing both the old history and the new changed one. But still all the merges are flattened. Your example works fine, though. I posted some gitk screenshots of before and after running the filter-branch command exactly as you specified. – GorillaPatch Feb 12 '12 at 19:58
0

This should do it for you (change realProjectDir to whatever that folder is named):

git filter-branch --prune-empty --subdirectory-filter realProjectDir master

Reference

Paul Armstrong
  • 7,008
  • 1
  • 22
  • 36
  • No that did not do anything. There are no double commits, but also the .git directory did not move in the right location. – GorillaPatch Feb 12 '12 at 17:03