157

I have a repo1 and repo2 on local machine. They are very similar, but the latter is some kind of other branch (repo1 is not maintained anymore).

/path/to/repo1 $ git log HEAD~5..HEAD~4
<some_sha> Add: Introduce feature X

How to apply changes made by commit <some_sha> in repo1 to repo2?

Do I need to prepare some patch, or is it possible to do some cherry-pick between the repos?

How about doing the same but for range of commits?

takeshin
  • 49,108
  • 32
  • 120
  • 164
  • 2
    Can't you just pull from repo1 to repo2? – zwol Sep 28 '10 at 18:51
  • for the slightly more specific case where you're looking to apply changes to a file or files that were moved in one of the repositories, look here: https://stackoverflow.com/questions/3491270/git-merge-apply-changes-to-code-that-moved-to-a-different-file – Braham Snyder Aug 11 '17 at 07:16

4 Answers4

249

You probably want to use git format-patch and then git am to apply that patch to your repository.

/path/to/1 $ git format-patch sha1^..sha1
/path/to/1 $ cd /path/to/2
/path/to/2 $ git am -3 /path/to/1/0001-…-….patch

Or, in one line:

/path/to/2 $ git --git-dir=/path/to/1/.git format-patch --stdout sha1^..sha1 | git am -3
Ohad Schneider
  • 36,600
  • 15
  • 168
  • 198
knittl
  • 246,190
  • 53
  • 318
  • 364
  • 10
    This solution proved simpler and safer than the accepted answer of direct cherry-picking using `GIT_ALTERNATE_OBJECT_DIRECTORIES` (that one would corrupt my repository). – Chuim Oct 06 '15 at 09:22
  • 3
    When there are conflicts it won't work because it fails to find the commits on the other branch. – Roger Far Aug 22 '17 at 20:03
  • 2
    Adding `--ignore-whitespace` to the `git am` command may resolve any conflicts and avoid needing to perform a 3-way merge – Hugheth May 01 '19 at 11:58
  • 2
    If you just want the latest commit: `git format-patch HEAD^1` – Boris Verkhovskiy Jul 09 '21 at 19:16
  • Note that this patch will be applied to the current branch in `/path/to/2`, so if you want to switch to the branch you made the commits on in the other repo, do that before running `git am -3 ...`. – BallpointBen Mar 04 '23 at 06:05
116

You can do cherry-pick if you add the second repo as a remote to the first (and then fetch).

akaihola
  • 26,309
  • 7
  • 59
  • 69
wRAR
  • 25,009
  • 4
  • 84
  • 97
  • 12
    That's actually the proper way to do it. – Wilbert Mar 28 '13 at 09:39
  • 5
    This feels like the right way to me as well. And I just used it and it worked well for me. – Ricky Nelson Mar 21 '14 at 16:42
  • 13
    I would rather say: do `git fetch [remote-name]` in the second repo and then `git cherry-pick [sha1]`. –  Dec 02 '14 at 20:22
  • 5
    This approach worked great for me, thanks. Since the second repo was also local, just had to use a file URI when adding it as a remote. – palimpsestor Mar 05 '15 at 06:13
  • 1
    Sounds like this really should be the best answer – shrimpwagon Jul 24 '15 at 15:17
  • 3
    In my case, I have two clones of a gigantic remote git repository (to allow parallel work), meaning all of its history is already downloaded and stored twice in my HD. If I should also have to add each as a remote of the other, that would create yet two extra copies of the same history and would potentially require syncs among them before I would be able to `cherry-pick`. So even though it might feel like the "right" way, it is not always the most practical. – Chuim Nov 19 '15 at 22:57
  • 1
    This really should be the best answer, just tried and it worked like a charm. – Filippo Bistaffa Aug 01 '16 at 11:56
  • I'm having a hard time when to know to use cherry-pick vs rebase. I rebased already, but not all the commits came with that rebase. It only rebased up to some common time. Now I am trying to move the, possibly, newer commits over. I think this answer sums that up. – blamb Dec 06 '17 at 19:53
  • Ok, that was my bad, I later realized that the commits weren't there because i was comparing my local copy of the remote repo, but fetching from remote copy, i needed to `git push` those commits up to the remote repo first, then fetch again. `rebase -i` is still the way to go for me. – blamb Dec 06 '17 at 20:23
  • 1
    If anybody wondering how to add a local repo as a remote to another: https://stackoverflow.com/a/10603750/9157799 – M Imam Pratama Jan 21 '22 at 18:23
35

As a hack, you can try modifying recipe for comparing commits in two different repositories on GitTips page, i.e.:

GIT_ALTERNATE_OBJECT_DIRECTORIES=../repo/.git/objects \
git cherry-pick $(git --git-dir=../repo/.git rev-parse --verify <commit>)

where ../repo is path to the other repository.

With modern Git you can use multiple revisions and revision ranges with cherry-pick.

The $(git --git-dir=../repo/.git rev-parse --verify <commit>) is here to translate <commit> (for example HEAD, or v0.2, or master~2, which are values in the second repository you copy from) into SHA-1 identifier of commit. If you know SHA-1 of a change you want to pick, it is not necessary.

NOTE however that Git can skip copying objects from source repository, as it doesn't know that the alternate object repository is only temporary, for one operation. You might need to copy objects from the second repository with:

GIT_ALTERNATE_OBJECT_DIRECTORIES=../repo/.git/objects git repack -a -d -f

This puts those objects borrowed from second repository in original repository storage

Not tested.


A not so hacky solution is to follow knittl answer:

  • Go to second repository you want to copy commits from, and generate patches from commits you want with git format-patch
  • Optionally, copy patches (0001-* etc.) to your repository
  • Use git am --3way to apply patches
Community
  • 1
  • 1
Jakub Narębski
  • 309,089
  • 65
  • 217
  • 230
  • 1
    Works good. If you have problems with commit then do 'git reset HEAD; git add .'. – gumik Oct 16 '12 at 09:53
  • 5
    this is awesome -- how would you do a range of commits? just sha1...sha2? – hvgotcodes Mar 06 '13 at 17:09
  • I also get `fatal: unable to read tree ...` but after `git reset HEAD^` everything works fine – jmarceli Feb 20 '14 at 00:50
  • @hvgotcodes it worked for me simply by passing the range as `` but the `rev-parse --verify` command doesn't like it as it accepts only single commit values. But as `cherry-pick` accepts both single and range commit values, I ask: why is `rev-parse` needed? – Chuim Oct 01 '15 at 15:37
  • 1
    @Chuim: `git rev-parse` is needed if you want to refer to a commit by its ref-based name in other repository, e.g. `master`, `HEAD^^`, or something like that; rev-parse turns it into universal SHA-1 identifier. – Jakub Narębski Oct 01 '15 at 19:49
  • Thanks @JakubNarębski. It seemed to work as the commits were applied and showed up in the log. But somehow the repository would get corrupted (missing blobs) and I got the same `fatal: unable to read ` described above. In my case `git reset HEAD` didn't help. – Chuim Oct 05 '15 at 14:37
  • @Chuim : One thing that could happen (but shouldn't) is that Git performs operation assuming that objects in temporary alternate are always available. You can try (after cherry-pick) `GIT_ALTERNATE_OBJECT_DIRECTORIES=../repo/.git/objects git repack -a -d -f` to put those objects in original repository storage, then `git gc`. HTH. – Jakub Narębski Oct 05 '15 at 20:20
  • Thanks again @JakubNarębski. I finally when the route of the `format-patch | am` answer which proved simpler, safer and directly accepts both single commits and ranges. – Chuim Oct 06 '15 at 09:21
  • Can a `.diff` be taken of the `repo1` and applied to the `repo 2` by way of `git apply`? – Apurva Kunkulol Oct 01 '19 at 12:19
  • @ApurvaKunkulol : Yes, `git am` uses `git apply` to apply the patch; however `git apply` would not create a commit, only apply changes, it is also limited in its ability to resolve conflicts (when patch no longer applies cleanly because file is different). – Jakub Narębski Oct 02 '19 at 10:22
6

I wrote a small script for applying the diff output of repo diff https://github.com/raghakh/android-dev-scripts/commit/a57dcba727d271bf2116f981392b0dcbb22734d0

Ragha
  • 61
  • 1
  • 1