Is it possible to cherry-pick a commit from another git repository?
Asked Answered
M

14

1070

I'm working with a git repository that needs a commit from another git repository that knows nothing of the first.

Typically I would cherry-pick using the HEAD@{x} in the reflog, but because this .git knows nothing of this reflog entry (different physical directory), how can I cherry-pick this, or can I?

I'm using git-svn. My first branch is using git-svn of the trunk of a Subversion repo, and the next branch is using git-svn on a Subversion branch.

Maciemaciel answered 25/2, 2011 at 16:47 Comment(0)
T
981

You'll need to add the other repository as a remote, then fetch its changes. From there you see the commit and you can cherry-pick it.

Like that:

git remote add other https://example.link/repository.git
git fetch other

Now you have all the information to simply do git cherry-pick.

When done, you may want to remove the remote again, if you don't need it any more, with

git remote remove other

More info about working with remotes here: https://git-scm.com/book/en/v2/Git-Basics-Working-with-Remotes

Titular answered 25/2, 2011 at 16:50 Comment(15)
What if I'm using git-svn? my first branch is using git-svn of the trunk and the next is using the git-svn on a branch (thanks for the quick reply)Maciemaciel
@gitcoder182: not sure since I never used git-svn, but it should work.Titular
when you first clone the Subversion repository make sure you clone the entire repository, not just the trunk. Also make sure you use the --stdlayout option of git-svn if you're using the standard trunk/branches/tags layout in Subversion. Then the Subversion branch will be a mere remote git branch.Choi
Is it really necessary to add it as a remote !?Commute
If you're using Github, you can pull the patch by appending .patch to the commit URL, and then applying it with git am < d821j8djd2dj812.patch. Outside of GH, similar concepts could be done as referenced in the alternative answer below.Wills
@Wills which answer below is the "alternative" one? Please link to it.Perform
Detailed steps to cherry pick from another repo: coderwall.com/p/sgpksw/git-cherry-pick-from-another-repositoryVoltage
You don't really need to add the remote. fetching is enough. And ../<some_other_repo>/ is a perfectly fine uri, if you have the repo on your disk already.Skatole
Then we can merge the "other" ( remote branch ) to current using \n git merge <remote_name>/<branch> e.g git merge other/master this merges the remote master to current masterLimitation
git remote rm other instead of git remote remove otherLarva
After removing the remote, you may need to prune stale tags. Use git fetch --prune --prune-tags to achieve this. See https://mcmap.net/q/12262/-remove-local-git-tags-that-are-no-longer-on-the-remote-repository for details about pruning tags.Circle
Hi, is there anything wrong with leaving 2 repos linked in this way on my local machine, if I occassionally want to cherry-pick something? I certainly don't want to affect anyone else's repo. Is pushing safe while having the 2nd remote added? Thanks.Enolaenormity
Doesn't work. Got: error: sha1 information is lacking or useless and error: could not build fake ancestor Sync
@Wills please add your comment as a proper answer!Clicker
CAUTION: this is very dangerous when both repo's have git tags. git fetch will fetch all references to tags in the other remote and if you use git push --tags later you are at risk of accidentally pushing all the tags from the other remoteWoollen
F
1095

The answer, as given, is to use format-patch but since the question was how to cherry-pick from another folder, here is a piece of code to do just that:

$ git --git-dir=../<some_other_repo>/.git \
format-patch -k -1 --stdout <commit SHA> | \
git am -3 -k

Explanation from Cong Ma comment Aug 28 '14

git format-patch command creates a patch from some_other_repo's commit specified by its SHA (-1 for one single commit alone). This patch is piped to git am, which applies the patch locally (-3 means trying the three-way merge if the patch fails to apply cleanly).

Fund answered 29/2, 2012 at 22:6 Comment(21)
@BenLee thanks for the Rep! I appreciate it. I use my method rather than the accepted answer for related forks when there is a mix of licenses involved. I control keeping the code clean.Fund
This is spot on, but it would be great if anyone could expand on this - a breakdown of exactly what's going on (especially with those flags) would be incredibly useful.Affranchise
@NickF, the git format-patch command creates a patch from some_other_repo's commit specified by its SHA (-1 for one single commit alone). This patch is piped to git am, which applies the patch locally (-3 means trying the three-way merge if the patch fails to apply cleanly). Hope that explains.Dictation
error: patch failed: somefile.cs:85 error: somefile.cs: patch does not apply Did you hand edit your patch? It does not apply to blobs recorded in its index. Cannot fall back to three-way merge. Patch failed at 0001 Added GUI parts. The copy of the patch that failed is found in: <some_other_repo>/.git/rebase-apply/patch When you have resolved this problem, run "git am --continue". If you prefer to skip this patch, run "git am --skip" instead. To restore the original branch and stop patching, run "git am --abort".Ja
Can a stash be specified in place of the "commit SHA"? In most git commands they are interchangeable, just want to confirm if that is true here as well. Especially since stashes are slightly different in that they have 2 parents (like merge commits).Twinberry
@Ja try using --ignore-whitespace. Full command: git --git-dir=../<some_other_repo>/.git format-patch -k -1 --stdout <commit SHA> | git am -3 -k --ignore-whitespacePipit
For me, git show works as an alternative to git format-patch. And git apply can be used instead of git am. Not sure why one would use one or the other.Atonal
Oh I see. git am actually commits the patch, with the commit message.Atonal
It seems that git format-patch accepts a --relative=.. parameter, even though this is not documented in man git format-patch. This allows to create the patches from a subdirectory.Atonal
The -1 is not required when creating a patch from a series of commits like git format-patch [..] aaa..bbb. Here the --relative=.. acts like a filter, so only the commits from this subdirectory are included in the patch. Similar to what one would otherwise do with filter-branch.Atonal
There's been a lot of upvotes for this and comments saying this is the better answer. It worked for me, but I would like to know why this is the better answer? What are it's advantages over the 'git remote' & cherry-pick approach?Encephalograph
@RobertWahler Any way to do this straight from another repo's commit on GitHub via curl piping? I've played with some different flavors of your command and haven't figured it out yet!Perique
@Encephalograph Because it's way simpler. Adding the remote and fetching brings in all of the other repo's changes. This command line is a one-time action.Neron
The -k isn't necessary for this, and adds complexity to the answer.Psalm
Can this git-dir's physical location be on another server? I mean, is there a way to specify something like: --git-dir=other_server:/path/to/repo/.git ?Tobiastobie
This only worked for me using --ignore-whitespace and it does not work on Windows at all.Villalba
This is not a generalized way. In my case, it turned out to be a 3-way merger. Refer this: #16572524Shrewmouse
Although this answer is simpler in theory, if you are cherry-picking one commit, you might find yourself wanting to cherry-pick more commits in the future. Adding another repo as a remote will make this a little bit easier in the future.Halm
Thank you for saving me from committing that useful local fix that has no place in the repo, yet required from another repo. Just cherry picked it from the on disk repo, --ignore-whitespace is needed btw. Maybe add it to the answer?Obfuscate
When fatal: sha1 information is lacking or useless error occurs, add --reject to last command (git am) and search repo for *.rej files. Those are unaplied changes. Apply them manually and use git add + git am --continue to proceed. See programmerall.com/article/9881500742Ayo
NB you can remove -1 and use refspecs like main..HEAD etc. Much more useful imhoAngulation
T
981

You'll need to add the other repository as a remote, then fetch its changes. From there you see the commit and you can cherry-pick it.

Like that:

git remote add other https://example.link/repository.git
git fetch other

Now you have all the information to simply do git cherry-pick.

When done, you may want to remove the remote again, if you don't need it any more, with

git remote remove other

More info about working with remotes here: https://git-scm.com/book/en/v2/Git-Basics-Working-with-Remotes

Titular answered 25/2, 2011 at 16:50 Comment(15)
What if I'm using git-svn? my first branch is using git-svn of the trunk and the next is using the git-svn on a branch (thanks for the quick reply)Maciemaciel
@gitcoder182: not sure since I never used git-svn, but it should work.Titular
when you first clone the Subversion repository make sure you clone the entire repository, not just the trunk. Also make sure you use the --stdlayout option of git-svn if you're using the standard trunk/branches/tags layout in Subversion. Then the Subversion branch will be a mere remote git branch.Choi
Is it really necessary to add it as a remote !?Commute
If you're using Github, you can pull the patch by appending .patch to the commit URL, and then applying it with git am < d821j8djd2dj812.patch. Outside of GH, similar concepts could be done as referenced in the alternative answer below.Wills
@Wills which answer below is the "alternative" one? Please link to it.Perform
Detailed steps to cherry pick from another repo: coderwall.com/p/sgpksw/git-cherry-pick-from-another-repositoryVoltage
You don't really need to add the remote. fetching is enough. And ../<some_other_repo>/ is a perfectly fine uri, if you have the repo on your disk already.Skatole
Then we can merge the "other" ( remote branch ) to current using \n git merge <remote_name>/<branch> e.g git merge other/master this merges the remote master to current masterLimitation
git remote rm other instead of git remote remove otherLarva
After removing the remote, you may need to prune stale tags. Use git fetch --prune --prune-tags to achieve this. See https://mcmap.net/q/12262/-remove-local-git-tags-that-are-no-longer-on-the-remote-repository for details about pruning tags.Circle
Hi, is there anything wrong with leaving 2 repos linked in this way on my local machine, if I occassionally want to cherry-pick something? I certainly don't want to affect anyone else's repo. Is pushing safe while having the 2nd remote added? Thanks.Enolaenormity
Doesn't work. Got: error: sha1 information is lacking or useless and error: could not build fake ancestor Sync
@Wills please add your comment as a proper answer!Clicker
CAUTION: this is very dangerous when both repo's have git tags. git fetch will fetch all references to tags in the other remote and if you use git push --tags later you are at risk of accidentally pushing all the tags from the other remoteWoollen
R
212

Here's an example of the remote-fetch-merge.

cd /home/you/projectA
git remote add projectB /home/you/projectB
git fetch projectB

Then you can:

git cherry-pick <first_commit>..<last_commit>

or you could even merge the whole branch(only if you really need to merge everything)

git merge projectB/master
Role answered 13/7, 2012 at 20:20 Comment(8)
git merge projectB/master is very, very wrong, because you're not applying the changes from a single commit (like a cherry-pick would), you're actually merging in all changes in projectB/master that aren't contained in your own master branch.Perform
It was my assumption that this was the original poster's intent. Otherwise, yes, this is not the right option for them.Role
This works brilliantly when the two repositories are related.Edam
I have created a copy from a git repository (just for "playing around" without breaking the original repo) and to keep it up-to-date with its source, the answer from Brian is exactly what I needed, so, Cupcake, I have to say, it's not "wrong", but another use case. But it is nice from you to point out the potential disaster :DDissuasive
IMO this needs to be the accepted solution. In addition, if you want to delete the remote after you're done cherry-picking from it, use git remote rm projectB. Also use git tag -d tag-name to remove any tags fetched from the remote repo. The remote commits will no longer show in your history, and pruning will remove them from storage eventually.Brita
I read that since git 2.9 you have to use the --allow-unrelated-histories. Using this option worked for me.Allyson
Unorthodox: copy files over and re-commit. Do this only if you are sure no changes would be discarded by overwriting.Alfredalfreda
-n means no-commit docs and I thing very important to see the changes before making a commit or mergeAlaric
C
195

You can do it, but it requires two steps. Here's how:

git fetch <remote-git-url> <branch> && git cherry-pick FETCH_HEAD

Replace <remote-git-url> with the url or path to the repository you want cherry-pick from.

Replace <branch> with the branch or tag name you want to cherry-pick from the remote repository.

You can replace FETCH_HEAD with a git SHA from the branch.

Updated: modified based on @pkalinow's feedback.

Coo answered 14/3, 2013 at 22:40 Comment(8)
It works with a branch name, but not with SHA. If you want to cherry-pick a commit denoted by its hash, use this instead: git fetch <repo-url> <branch> && git cherry-pick <sha>.Sable
Thanks. This was just what I needed to insert a number of commits from one repository to another, which I created for this purpose.Titanesque
This was exactly what I needed with many custom implementation of our code for different clients (who each have their own repositories/forks) we needed a way to get specific commits into our base/trunk. THANKS!Pinniped
This should be the accepted answer for a one-shot cherry pick across repos. I use it all the time when picking between repos that are already local, the remote URL is then just a local filesystem path.Lianne
If you are mostly familiar with cherry-pick this is probably the simplest. You don't need to add the full remote (although you can, if you are going to do this a lot). Just fetch the branch you need to get a local reference (can be from another local directory, or remote URL), and then cherry-pick the commits.Lunchroom
This is the wayCaryophyllaceous
Will this leave any remains that should be removed after the cherry-picking has taken place? And how to do that?Rocambole
To get the remote url, use this command git config --get remote.origin.url Castleman
L
68

Here are the steps to add a remote, fetch branches, and cherry-pick a commit

# Cloning our fork
$ git clone [email protected]:ifad/rest-client.git

# Adding (as "endel") the repo from we want to cherry-pick
$ git remote add endel git://github.com/endel/rest-client.git

# Fetch their branches
$ git fetch endel

# List their commits
$ git log endel/master

# Cherry-pick the commit we need
$ git cherry-pick 97fedac

Source: https://coderwall.com/p/sgpksw

Levins answered 16/9, 2014 at 20:16 Comment(0)
M
18

See How to create and apply a patch with Git. (From the wording of your question, I assumed that this other repository is for an entirely different codebase. If it's a repository for the same code base, you should add it as a remote as suggested by @CharlesB. Even if it is for another code base, I guess you could still add it as a remote, but you might not want to get the entire branch into your repository...)

Mahler answered 25/2, 2011 at 16:51 Comment(0)
M
13

You can do it in one line as following. Hope you are in git repository which need the cherry-picked change and you have checked out to correct branch.

git fetch ssh://[email protected]:7999/repo_to_get_it_from.git branchToPickFrom && git cherry-pick 02a197e9533
# 

git fetch [branch URL] [Branch to cherry-pick from] && git cherry-pick [commit ID]

Monocycle answered 17/9, 2015 at 9:47 Comment(0)
C
6

Yes. Fetch the repository and then cherry-pick from the remote branch.

Choi answered 25/2, 2011 at 16:51 Comment(0)
C
6

Here is one easy to type out from memory, inspired by the comment of @radicand. It is dependent on the capabilities of the forge, but Github, Gitlab and Gitea definitely support it.

You append .patch to the commit URL and apply it via git am:

curl --location URL.patch | git am

--location makes it follow redirects, which can happen when e.g. copying a patch from a pull request

Clicker answered 3/1, 2023 at 11:59 Comment(0)
R
5

If the other repo is present on the same machine, you could achieve a similar effect as cherry-pick by applying a patch and then committing the original message. Here is an example:

$ git apply <(git -C "$PATH_TO_OTHER_REPO" show "$COMMIT_HASH")
$ MSG="$(git -C "$PATH_TO_OTHER_REPO" log -n 1 --pretty=format:'%s' "$COMMIT_HASH")"
$ git commit -m "$MSG"

I don't have to do it very often so I'm fine with this workflow. However, it should be fairly easy to compose a customized Git command for this and have it nicer and more automated.

Notice that the command inside <(...) can be anything that generates a valid patch for Git: git show, git diff, using wget or curl to fetch raw diff contents from a remote such as Github (so you can skip cloning), cat from a file... That line, by itself, is extremely useful.

Repeal answered 30/6, 2022 at 18:12 Comment(0)
B
2

Assuming A is the repo you want to cherry-pick from, and B is the one you want to cherry-pick to, you can do this by adding </path/to/repo/A/>/.git/objects to </path/to/repo/B>/.git/objects/info/alternates. Create this alternates files if it does not exist.

This will make repo B access all git objects from repo A, and will make cherry-pick work for you.

Bar answered 8/4, 2016 at 19:14 Comment(0)
V
2

For the case, that the commit has to be "cherry-picked" for some localized validations. It could also be, for same repository checkout, but with local commits (i.e. not pushed yet to server).

E.g.

  • repo1 similar to repo2
  • repo1 has branch b1 - HEAD progressed by 2 commits (including commit_x) locally.
  • repo2 has branch bb1 - required to cherry-pick commit_x.

For this,

$ cd repo2

repo2 $ git fetch <path_to_repo1>

repo2 $ git cherry-pick <commit_x>

In the above case, the commit_x will now be identifiable and picked up (by the aid of fetch).

Voguish answered 12/7, 2021 at 9:32 Comment(0)
G
0

My situation was that I have a bare repo that the team pushes to, and a clone of that sitting right next to it. This set of lines in a Makefile work correctly for me:

git reset --hard
git remote update --prune
git pull --rebase --all
git cherry-pick -n remotes/origin/$(BRANCH)

By keeping the master of the bare repo up to date, we are able to cherry-pick a proposed change published to the bare repo. We also have a (more complicated) way to cherry-pick multiple braches for consolidated review and testing.

If "knows nothing" means "can't be used as a remote", then this doesn't help, but this SO question came up as I was googling around to come up with this workflow so I thought I'd contribute back.

Gastropod answered 31/5, 2013 at 19:4 Comment(1)
-n means no-commit according to git docs and I thing very important to see the changes before making a commitAlaric
S
0

If you want to cherry-pick multiple commits for a given file until you reach a given commit, then use the following.

# Directory from which to cherry-pick
GIT_DIR=...
# Pick changes only for this file
FILE_PATH=...
# Apply changes from this commit
FIST_COMMIT=master
# Apply changes until you reach this commit
LAST_COMMIT=...

for sha in $(git --git-dir=$GIT_DIR log --reverse --topo-order --format=%H $LAST_COMMIT_SHA..master -- $FILE_PATH ) ; do 
  git --git-dir=$GIT_DIR  format-patch -k -1 --stdout $sha -- $FILE_PATH | 
    git am -3 -k
done
Several answered 23/1, 2020 at 15:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.