Git - Moving Commits Between Repositories

4 minute read

I use a local git repository as my scratch/temp workspace when playing around with and developing scripts. I developed this habit a couple of years ago and have stuck with it fairly successfully.

A simple PowerShell script I had been toying with recently, ended up evolving into something that can be used in another project. The script, along with commit history, was in my non-public personal repository. I needed to move it to a public, project related, repository. Preferrably, along with commit history.

If you were to run a quick google search, it would turn up a couple of ways to accomplish this seemingly simple task.

  1. Add the source repo as a remote, fetch, cherry-pick, merge, etc. You can find a comprehensive example of this approach describe by Andrew Marcinkevičius here
  2. Export desired commits as patches and import them into the destination repositories.

After playing around with both, I found the 2nd approach to be simpler and dummy-proof.

Exporting Commits

git provides format-patch command, which can export an entire commit in a .patch file. The file is unix mailbox format and contains everything related to the commit, including

Excerpt from documentation:

Name:

git-format-patch - Prepare patches for e-mail submission

Description:

Prepare each commit with its patch in one file per commit, formatted to resemble UNIX mailbox format. The output of this command is convenient for e-mail submission or for use with git am.

There are two ways to specify which commits to operate on.

  1. A single commit, , specifies that the commits leading to the tip of the current branch that are not in the history that leads to the to be output.
  2. Generic expression (see "SPECIFYING REVISIONS" section in gitrevisions(7)) means the commits in the specified range.

Syntax:

git format-patch --output-directory "../patches" FIRST_COMMIT_SHA1~..LAST_COMMIT_SHA1 

This command will export all commits between FIRST_COMMIT_SHA1 and LAST_COMMIT_SHA1, inclusive, and create one .patch file per commit in the ../patches directory. The .. and ~.. are standard operators that can be used to specify sets of revisions in git. This can be any of supported methods of specifying ranges. In this case, the ~.. ensures that the revision set will include the starting commit along with the rest.

You can get more information on revision sets and their usage by typing in the following command:

git help revisions

Importing Commits

Applying the patch to a different repo is straight forward. The following command will accomplish this task:

git am 0001-Example-Patch-File.patch

You can export multiple patches at once by using wildcards, e.g.

git am *.patch

Full Example

I’m working within powershell for the following exmaple, but it should work similarly in batch/command prompt.

Create a couple of test repos and a directory for patches

C:\Test> New-Item "repo1","repo2","patches" -ItemType Directory

Initialize both repos:

C:\Test> cd .\repo1
C:\Test\repo1> git init
Initialized empty Git repository in C:/Test/repo1/.git/

C:\Test\repo1 [master]> cd ..\repo2

C:\Test\repo2> git init
Initialized empty Git repository in C:/Test/repo2/.git/

C:\Test\repo2 [master]>

Create a few commits in both repos:

# repo 1
C:\Test\repo1 [master]> 0..10 | % { $file = "file_{0:D2}.md" -f $_; "## Test File - ${_}" | Set-Content -Path $file; git add $file; git commit -m "Add file ${_}"; }
[master (root-commit) 23fdd70] Add file 0
 1 file changed, 1 insertion(+)
 create mode 100644 file_00.md
[master 0f5d2a6] Add file 1
...
...

# repo 2
C:\Test\repo2 [master]> 50..55 | % { $file = "file_{0:D2}.md" -f $_; "## Test File - ${_}" | Set-Content -Path $file; git add $file; git commit -m "Add file ${_}"; }
[master (root-commit) a27de5b] Add file 50
 1 file changed, 1 insertion(+)
 create mode 100644 file_50.md
...
...

Export patches for file 3 through 5 from repo1:

C:\Test\repo1 [master]> git format-patch --output-directory ..\patches 488e67c~..ff35966
..\patches/0001-Add-file-3.patch
..\patches/0002-Add-file-4.patch
..\patches/0003-Add-file-5.patch
..\patches/0004-Add-file-6.patch

Import into repo2:

C:\Test\repo2 [master]> Get-ChildItem ..\patches\*.patch | % { git am $_.FullName }
Applying: Add file 3
Applying: Add file 4
Applying: Add file 5
Applying: Add file 6

Repo2 now has the files 3 through 6, along with original commit date, commit message, and the content.

Please note that commit hash will change when the patch is applied. You will not retain the original commit hash from the source repo.

Leave a Comment