git: Apply changes introduced by commit in one repo to another repo
Asked Answered
S

4

161

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?

Socinus answered 28/9, 2010 at 18:49 Comment(2)
Can't you just pull from repo1 to repo2?Yare
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: #3491770Basinet
C
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
Czech answered 28/9, 2010 at 21:49 Comment(10)
Works good. If you have problems with commit then do 'git reset HEAD; git add .'.Turntable
this is awesome -- how would you do a range of commits? just sha1...sha2?Declinometer
I also get fatal: unable to read tree ... but after git reset HEAD^ everything works fineSeljuk
@Declinometer it worked for me simply by passing the range as <commit> 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?Nestling
@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.Wennerholn
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 <SHA1> described above. In my case git reset HEAD didn't help.Nestling
@Nestling : 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.Wennerholn
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.Nestling
Can a .diff be taken of the repo1 and applied to the repo 2 by way of git apply?Courtland
@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).Wennerholn
W
256

You can to use git format-patch in your first repository and then git am to apply that patch in your second 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
Wattmeter answered 28/9, 2010 at 19:23 Comment(5)
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).Nestling
When there are conflicts it won't work because it fails to find the commits on the other branch.Dogs
Adding --ignore-whitespace to the git am command may resolve any conflicts and avoid needing to perform a 3-way mergeEgidio
If you just want the latest commit: git format-patch HEAD^1Solace
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 ....Detergent
D
119

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

Deemphasize answered 28/9, 2010 at 19:29 Comment(10)
That's actually the proper way to do it.Taboo
This feels like the right way to me as well. And I just used it and it worked well for me.Ambsace
I would rather say: do git fetch [remote-name] in the second repo and then git cherry-pick [sha1].Validate
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.Dichy
Sounds like this really should be the best answerLateshalatest
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.Nestling
This really should be the best answer, just tried and it worked like a charm.Homeland
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.Nonna
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.Nonna
If anybody wondering how to add a local repo as a remote to another: https://mcmap.net/q/92634/-how-to-add-a-local-repo-and-treat-it-as-a-remote-repoPresignify
C
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
Czech answered 28/9, 2010 at 21:49 Comment(10)
Works good. If you have problems with commit then do 'git reset HEAD; git add .'.Turntable
this is awesome -- how would you do a range of commits? just sha1...sha2?Declinometer
I also get fatal: unable to read tree ... but after git reset HEAD^ everything works fineSeljuk
@Declinometer it worked for me simply by passing the range as <commit> 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?Nestling
@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.Wennerholn
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 <SHA1> described above. In my case git reset HEAD didn't help.Nestling
@Nestling : 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.Wennerholn
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.Nestling
Can a .diff be taken of the repo1 and applied to the repo 2 by way of git apply?Courtland
@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).Wennerholn
T
6

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

Thoraco answered 16/2, 2012 at 23:45 Comment(1)
How do get the diff output to feed to your script?Homophonous

© 2022 - 2024 — McMap. All rights reserved.