Extracting git Changes to a Separate Repository
“I really need to extract all changes (ever) to this file and move them into a different repository.”
– pretty much nobody ever
But what if…
In my case, I had written a micro-library as part of one project (repo) and then later realized that I’d like it to be separated out into it’s own repo. This included ~10 commits to ~3 files, over the course of over a year. Here’s how I did it…
1. Create the new repo, with backdating
❯ mkdir jx
cd jx
❯ git new
...
❯ git commit --amend --allow-empty --date '<paste first commit date>'
...
-
newis an alias to create a new repo with an empty commit (or whatever contents are already present) -
the date was taken from
git show --name-only <commit>in the old project, or from the first patch file
2. Find the commits to extract
❯ git lol -- public/js/jx.js
23ecb5d (1 year, 9 months ago) feat(js): add jx micro-library - Clayton Carter
330c719 (1 year, 9 months ago) feat(jx): add computed props - Clayton Carter
e59b878 (1 year, 9 months ago) feat(jx): add support for reactive data objects - Clayton Carter
3a80ffa (1 year, 9 months ago) feat(jx): add comments - Clayton Carter
eba1567 (1 year, 4 months ago) test(jx): add test for jx-data-class - Clayton Carter
2f00146 (9 months ago) feat(jx): add jx-init attribute - Clayton Carter
d8f3e6f (8 months ago) refactor(jx): remove dead code - Clayton Carter
-
lolis an alias forlog --one-lineor something like that
3. Extract the commits as patches
You can probably tell by the dates above, but the first 4 commits where a consecutive series, and the rest were individuals.
❯ git format-patch 23ecb5d~..3a80ffa
0001-feat-js-add-jx-micro-library.patch
0002-feat-jx-add-computed-props.patch
0003-feat-jx-add-support-for-reactive-data-objects.patch
0004-feat-jx-add-comments.patch
❯ git format-patch -1 --start-number=5 eba1567
0005-test-jx-add-test-for-jx-data-class.patch
❯ git format-patch -1 --start-number=6 2f00146
0006-feat-jx-add-jx-init-attribute.patch
❯ git format-patch -1 --start-number=7 d8f3e6f
0007-refactor-jx-remove-dead-code.patch
-
note the
~(first parent operator) in that first line: I wanted the commit range to include commit23ecb5d, but..excludes the given commit
4. Move the patches to the new project, and clean them up
❯ mv 00*patch ~/src/jx
❯ rp 00\* website/public/js/ '' -w
❯ rp 00\* website/resources/js/ '' -w
This is the heavy lifting, but rp makes it not very
heavy! rp is an alias for
rep, a tool to make find and replace easy and fast from the command
line. I use it all the time and love it! But what is it really
doing?
In the original project, the files were located in different
directories than where I want them to be in the new project. These
calls to rp tell it find occurances of
website/public/js/ in all 00* files (glob
matched, not regex matched) and remove them (replace them with an
empty string). In other words:
website/public/js/jx.js→jx.js-
website/resources/js/tests/jx.spec.js→tests/jx.spec.js
This is roughly equivalent to
sed -i s/website\/public\/js\///g <file> for each
file.
But why? The original patches each have sections that look like this:
diff --git a/website/public/js/jx.js b/website/public/js/jx.js
index f79ce90..55a077b 100644
--- a/website/public/js/jx.js
+++ b/website/public/js/jx.js
@@ -1,83 +1,125 @@
... followed by diff contents
By editing these patches, we are basically just moving/renaming the files before they’re even created in the new repo. The edited patch will end up looking like this:
diff --git a/jx.js b/jx.js
index f79ce90..55a077b 100644
--- a/jx.js
+++ b/jx.js
@@ -1,83 +1,125 @@
... followed by diff contents
Which will put the new jx.js file will in the root of
the new repo.
5. Remove unwanted changes (optional)
In my case, one of the commits (patches) included a small edit to a file that I don’t want or need in the new project. The patch looked like this:
+ ... preceded by other diff contents
diff --git a/website/webpack.mix.js b/website/webpack.mix.js
index c60ff49..34927f3 100644
--- a/website/webpack.mix.js
+++ b/website/webpack.mix.js
@@ -38,6 +38,7 @@ const jsAssets = [
'fedco',
'form-spinner',
'../vendor/big',
+ 'jx',
];
const cssAssets = [
--
2.48.1
I deleted everything from diff through (including) the
line that includes const cssAssets, just above the
-- line. (I don’t know if those last 2 lines are
important, but I didn’t want to find out, so I left them. That last
one looks like my git version.)
The edited diff looked like this:
+ ... preceded by other diff contents
--
2.48.1
Presumably, if the diff-to-delete wasn’t at the end of the patch,
you would just delete up to the next line that starts with
diff.
6. Apply the Patches
Now just run
git am < 0001-feat-js-add-jx-micro-library.patch for
every patch, or do a batch patch with
for patch in 00*; git am < $patch; done.
7. Done!
Check your work:
❯ tree
.
├── jx.js
└── tests
└── jx.spec.js
1 directory, 2 files
❯ git lol
042090b (1 year, 9 months ago) Start project; initial commit - Clayton Carter
5c6d869 (1 year, 9 months ago) feat(js): add jx micro-library - Clayton Carter
c322339 (1 year, 9 months ago) feat(jx): add computed props - Clayton Carter
76bc184 (1 year, 9 months ago) feat(jx): add support for reactive data objects - Clayton Carter
85e982a (1 year, 9 months ago) feat(jx): add comments - Clayton Carter
dec3324 (1 year, 4 months ago) test(jx): add test for jx-data-class - Clayton Carter
c5cb813 (9 months ago) feat(jx): add jx-init attribute - Clayton Carter
aa71f87 (8 months ago) refactor(jx): remove dead code - Clayton Carter (HEAD -> main, origin/main, origin/HEAD)
That’s it! Yak: shaved.