Moving part of a git repository’s history into another repository
I’ve already created the new repository, and added the old one from the filesystem as a ‘remote,’ and created a new “root commit” (just adding a README for the new single-file project.) Now I need to transplant the commits pertaining to this particular file onto that new root-commit.
(I should mention that at no point was this file modified in the same commit as any other files; I suspect that may make this task slightly easier.)
4 Solutions collect form web for “Moving part of a git repository’s history into another repository”
This is based on an example in the filter-branch manpage:
git filter-branch --index-filter 'git ls-files -s | grep $'\t'<file-to-keep>$ | \ GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info && \ mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE' --prune-empty -- --all
The index filter prints the current contents of the index using
git ls-files -s, greps out only the file to keep (that grep is fairly obsessive – the fields are tab-delimited, with the filename being the last one), then creates a new index using that information, and moves it on top of the old one.
--prune-empty option causes filter-branch to remove any commits which now do nothing (i.e. they only touched other files), and the
-- --all tells it to rewrite all refs.
As always with filter-branch, it’s best to do this in a fresh clone, so if you screw anything up really badly you’re safe, even though filter-branch does keep backups in refs/originals. You might also want to read the checklist for shrinking a repository in the manpage; the upshot is that once you’re done, the best way to actually get rid of all the stuff you no longer need is to simply clone the filtered repository.
This will actually work even if the file was modified in the same commits as other files, though I suppose you could try to be sneaky and take advantage of that fact by simply generating patches for all commits which did touch that file, then going and constructing a new repository by applying those patches… but why bother?
(Side note: it’s way easier to remove a single file than to keep a single file. All you have to do in that case is use
git rm --cached --ignore-unmatch <filename> for an index filter.)
I ended up using the responses from this (otherwise irrelevant) post to construct a solution. Instead of rebasing into a tree with a new root, I modified the old root and
--onto-rebased the content to the new root:
Can I remove the initial commit from a Git repo?
A solution in a different vein – add the old repo as another remote, and then rebase and cherry-pick can work.
git clone old_repo git clone new_repo cd new_repo git remote add temp_repo old_repo git fetch temp_repo # bring the changed from old_repo into new_repo git rebase --onto <...> temp_repo/master OR git cherry-pick [list of relevant commits] git remote rm temp_repo git push origin HEAD
Having tried various approaches to move a file or folder from one Git repository to another, the only one which seems to work reliably is outlined below.
It involves cloning the repository you want to move the file or folder from, moving that file or folder to the root, rewriting Git history, cloning the target repository and pulling the file or folder with history directly into this target repository.
Make a copy of repository A as the following steps make major
changes to this copy which you should not push!
git clone --branch <branch> --origin origin --progress -v <git repository A url> eg. git clone --branch master --origin origin --progress -v https://username@giturl/scm/projects/myprojects.git
(assuming myprojects is the repository you want to copy from)
cd into it
cd <git repository A directory> eg. cd /c/Working/GIT/myprojects
Delete the link to the original repository to avoid accidentally
making any remote changes (eg. by pushing)
git remote rm origin
Go through your history and files, removing anything that is not in
directory 1. The result is the contents of directory 1 spewed out
into to the base of repository A.
git filter-branch --subdirectory-filter <directory> -- --all eg. git filter-branch --subdirectory-filter subfolder1/subfolder2/FOLDER_TO_KEEP -- --all
For single file move only: go through what’s left and remove
everything except the desired file. (You may need to delete files
you don’t want with the same name and commit.)
git filter-branch -f --index-filter \ 'git ls-files -s | grep $'\t'FILE_TO_KEEP$ | GIT_INDEX_FILE=$GIT_INDEX_FILE.new \ git update-index --index-info && \ mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE || echo "Nothing to do"' --prune-empty -- --all
eg. FILE_TO_KEEP = pom.xml to keep only the pom.xml file from FOLDER_TO_KEEP
git reset --hard
git gc --aggressive
You may want to import these files into repository B within a directory not the root:
Make that directory
mkdir <base directory> eg. mkdir FOLDER_TO_KEEP
Move files into that directory
git mv * <base directory> eg. git mv * FOLDER_TO_KEEP
Add files to that directory
git add .
Commit your changes and we’re ready to merge these files into the
Make a copy of repository B if you don’t have one already
git clone <git repository B url> eg. git clone https://username@giturl/scm/projects/FOLDER_TO_KEEP.git
(assuming FOLDER_TO_KEEP is the name of the new repository you are copying to)
cd into it
cd <git repository B directory> eg. cd /c/Working/GIT/FOLDER_TO_KEEP
Create a remote connection to repository A as a branch in repository
git remote add repo-A-branch <git repository A directory>
(repo-A-branch can be anything – it’s just an arbitrary name)
eg. git remote add repo-A-branch /c/Working/GIT/myprojects
Pull from this branch (containing only the directory you want to
move) into repository B.
git pull repo-A-branch master
The pull copies both files and history. Note: You can use a merge instead of a pull, but pull works better.
Finally, you probably want to clean up a bit by removing the remote
connection to repository A
git remote rm repo-A-branch
Push and you’re all set.