Git extract single file with history
Tags: git
Published: 2019-08-11 11:10:26

We have one python module that we want to split into it's own repo as we start using it in more than one project.

The first result when I searched for "git extract file with history" is from [Stackoverflow question][1]. But it doesn't really work when I try. All the branches and tags still retained. And since the approach is "filter out everything you don't want and keep what you want", it's quite slow on a large repo with very long history.

Someone also created an extension called `[git-splits][2]` inspired from another [SO's question][3]. But that extension seem more on extracting directory instead of single file.

Side-notes: One thing I like about git extensions is that you just create an executable called `git-something` and add it to your `$PATH` and then it will be available `git something`. That's one example of super simple extension API. I've created a command called `git chlog` that will show pretty changelog given certain tag.

Ok, back to extracting single file. Finally I found this [blog post][4]. It use `git am` approach, that by generating a patch from commits related to file, and the replaying it back in order to reconstruct the file from it's initial state to the current state.

```
git log --pretty=email --patch-with-stat --reverse --full-index --binary -- src/project/pgq.py > /tmp/patch
```

We can then apply this patch to get a new pgq.py with it's history from the original repo:-

```
git init
git touch setup.py
git add setup.py
git commit -m'Initial'
git am < /tmp/patch
```

Ok, this is the not so good part. I have to commit new file first there because when applying the patch, it failed half-way with error:-

```
/home/kamal/git/pgq/.git/rebase-apply/patch:38: trailing whitespace.

/home/kamal/git/pgq/.git/rebase-apply/patch:42: trailing whitespace.

warning: 2 lines add whitespace errors.
Applying: XT-596 cli function improved and tested
/home/kamal/git/pgq/.git/rebase-apply/patch:32: trailing whitespace.

/home/kamal/git/pgq/.git/rebase-apply/patch:47: trailing whitespace.

/home/kamal/git/pgq/.git/rebase-apply/patch:53: trailing whitespace.

warning: 3 lines add whitespace errors.
Applying: XT-596 fix ask_confirmation in pgq purge
Applying: fixed TypeError: not all arguments converted during string formatting
Patch is empty.  Was it split wrong?
If you would prefer to skip this patch, instead run "git am --skip".
To restore the original branch and stop patching run "git am --abort".
```

If I run `git am --skip` as suggested but I don't have initial commit yet in the new repo, I'll get this error:-

```
cat: /home/kamal/git/pgq/.git/ORIG_HEAD: No such file or directory
```

By adding initial commit, I can get passed this and run `git am --skip` repeatedly until all patches being applied. Of course the resulting history will be a little bit weird as the first commit in this repo seem appear at much later date than the first commit of the original history.

But other than that, it seem to work and I managed to get the file with all history intact. If you have better suggestion, feel free to share in the comment.

[1]:https://stackoverflow.com/questions/7375528/how-to-extract-one-file-with-commit-history-from-a-git-repo-with-index-filter
[2]:https://github.com/ajdruff/git-splits
[3]:https://stackoverflow.com/questions/5998987/splitting-a-set-of-files-within-a-git-repo-into-their-own-repository-preserving
[4]:https://www.pixelite.co.nz/article/extracting-file-folder-from-git-repository-with-full-git-history/