views:

597

answers:

3

Hi,

I have the remotes Foo and Bar. Foo is a web application that has lots of directories, relevent amongst them is /public which cointains assorted files and other directories.

Bar is a set of libraries and whatnot used on the front end, as such, it should go in /public/bar in Foo. Foo has no files there.

That would all be piece of cake with either submodules or subtree merge. However…

Bar's tree is messy, it has all sorts of pre-production files like PSDs and FLAs, and the only really useful part of it is what is inside its /www/tools.

So, what I want to do is merge Bar's /www/tools into Foo's /public/bar, and pretend the rest of Bar's tree doesn't even exist.

Can do?

(I would suppose this is very similar to how you merge from a project that originally merged yours as a subtree. Which I don't know how to do, either.)

A: 

I don't think this can be done using a merge, per-se, but these steps might do the trick, at least as far as the end product is concerned. First:

% git fetch Bar

This fetches the latest commit(s) from the Bar repository, but doesn't try to merge them. It records the SHA for the commit in .git/FETCH_HEAD

% cat .git/FETCH_HEAD
b91040363160aab4b5dd46e61e42092db74b65b7                branch 'Bar' of ssh://blah...

This shows what the SHA identifier for the latest commit from the remote branch is.

% git checkout b91040363160aab4b5dd46e61e42092db74b65b7 www/tools

This takes the copy of the files in www/tools for the fetched commit and overwrites what's in the working tree. You can then commit those changes to your local repository like normal. The resulting commit won't have any reference to where it came from, but it should at least get your repository with the versions of the files you want.

Brian Webster
Yeah I'd be happier keeping history. Maybe something could be done with filter-branch.
kch
1.) you can use "git checkout FETCH_HEAD www/tools" 2.) probably you can use "git checkout --merge FETCH_HEAD www/tools" 3.) after recording required state in working directory, you can create merge by hand by writing FETCH_HEAD to .git/MERGE_HEAD before git-commit
Jakub Narębski
Ah, I knew there had to be a better way of doing the FETCH_HEAD part
Brian Webster
+2  A: 

Edit: It occurs to me that you could do this just with git merge --no-commit. This will attempt the merge, and even if it does not conflict, it will stop just before committing. At this point you can remove all the junk you don't need (including restoring conflicted files if necessary) and create a merge commit only containing the desired subtree.

Original answer:

You can indeed use filter-branch for this. An outline:

Clone your source repo:

git clone --bare /path/to/bar /path/to/bar_clone

Using a bare clone will save you the time and space of creating a working directory.

Next, use filter-branch on the clone:

git filter-branch --index-filter 'git rm -rf <unwanted files/directories>' -- --all

The --all lets it know that you want to use all refs, not just the current HEAD. You will now have a repository containing only the desired subdirectory, and all of the history associated with it.

Note: Sorry, I don't know a really straightforward way to remove all but what you want. You have to be careful with wildcards because you don't want to clobber any git directories. Here's something that'll work, though it's slower, especially if you've got a lot of files:

git filter-branch --index-filter 'git rm -f `git ls-files | grep -v ^www/tools`' -- --all

Anyway, however you manage the listing of files to remove, you can go ahead with your subtree merge, pulling from bar_clone into foo.

Jefromi
A: 

Try using git subtree for this. It allows you to extract a subtree of a project into another project, which you can then merge into yours.

apenwarr