Skip to content

Instantly share code, notes, and snippets.

@davestewart
Last active April 25, 2020 21:24
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save davestewart/1273ca0d1615c79879a9e61167961210 to your computer and use it in GitHub Desktop.
Save davestewart/1273ca0d1615c79879a9e61167961210 to your computer and use it in GitHub Desktop.
Git: Extract feature to new repo, retaining history

Git: Extract a feature to a new repo, retaining history

Abstract

I recently needed to extract a feature from a project to its own repo so I could publish as a standalone library:

# from
/some-project/src/some-folder/some-feature/*

# to
/some-package/src/*

Additionally, I wanted to retain the history so the new repo would show the full development process.

The Git command to accomplish this would have been filter-branch but it has been depreciated in favour of a Python addon to Git called git filter-repo.

The addon did exactly what I wanted, but took quite a lot of experimentation to crack (mainly because of moving from src to src) so I've outlined the process and steps below so you can see how it is done.

Setup

The addon requires Git 2.22.0 as a minimum, so check your version and upgrade if needs be:

git --version

Then install the addon. I found the easiest way was via the Python package installer:

pip3 install git-filter-repo

Steps

Before looking the code, let's look at the steps the code takes.

  • clone the repo that contains the feature to a new folder
  • remove the origin to prevent accidentally changing the original repo
  • optionally checkout a branch if you need to
  • run git filter-repo to:
    • filter the original repo to the feature folder
    • reparent the folder to a temp root folder tmp (because from and to folders are both named src)
    • rename the temp folder to src
    • use the force flag because we removed the origin
  • optionally rename any checked out branch if you need to
  • optionally delete all tags

Code

Here is the code using placeholders so you can understand how it works:

git clone <repo_url> <package_name>
cd <package_name>
git remote rm origin
git checkout <branch name> # optional
git filter-repo \
    --path <feature_path> \
    --to-subdirectory-filter tmp \
    --path-rename tmp/<feature_path>:src \
    --force
git branch -m master # optional
git tag | xargs git tag -d #optional

Things to note:

  • the folder names tmp and src should be changed if you need to (because of conflicts, or you want a different structure)
  • the rename format is old_name:new_name so feel free to change this if your use case is different
  • the slashes allow the code to be written using multiple lines

This is the same code using concrete examples so you can see how it should look:

git clone https://github.com/some-user/some-project.git some-package
cd some-package
git remote rm origin
git checkout some-branch # optional
git filter-repo \
    --path src/some-folder/some-feature \
    --to-subdirectory-filter tmp \
    --path-rename tmp/src/some-folder/some-feature:src \
    --force
git branch -m master # optional
git tag | xargs git tag -d #optional

Result

You should now have a brand new repo with full commit history and a folder structure like so:

+- some-package
    +- src
        +- *   

Add your readmes, bundlers, etc. then you're done!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment