ChatGPT解决这个技术问题 Extra ChatGPT

How to merge a specific commit in Git

I have forked a branch from a repository in GitHub and committed something specific to me. Now I found the original repository had a good feature which was at HEAD.

I want to merge it only without previous commits. What should I do? I know how to merge all commits:

git branch -b a-good-feature
git pull repository master
git checkout master
git merge a-good-feature
git commit -a
git push
If you're trying to do this in relation to github, this article will step you through it. markosullivan.ca/how-to-handle-a-pull-request-from-github

L
Legorooj

'git cherry-pick' should be your answer here.

Apply the change introduced by an existing commit.

Do not forget to read bdonlan's answer about the consequence of cherry-picking in this post:
"Pull all commits from a branch, push specified commits to another", where:

A-----B------C
 \
  \
   D

becomes:

A-----B------C
 \
  \
   D-----C'

The problem with this commit is that git considers commits to include all history before them Where C' has a different SHA-1 ID. Likewise, cherry picking a commit from one branch to another basically involves generating a patch, then applying it, thus losing history that way as well. This changing of commit IDs breaks git's merging functionality among other things (though if used sparingly there are heuristics that will paper over this). More importantly though, it ignores functional dependencies - if C actually used a function defined in B, you'll never know.


@openid000: "more fine grained branches": which is indeed exactly what bdonlan suggested in his answer.
Note: "git rebase" also changes SHA-1. See also "git rebase vs. git merge ( stackoverflow.com/questions/804115/git-rebase-vs-git-merge ) and "git workflow" ( stackoverflow.com/questions/457927/… ) for cases where "git rebase" is legitimate.
Between "fine grained branches", "cherry-pick" and "rebase", you will then have all the possibilities for managing code in branches with git.
But note that when you merge, both the commits C' and C will prevail in the commit history.
This answer is not a merge: the resulting commit doesn't have ancestry pointing back to the merged commit. Maybe the OP didn't care about creating a merge per se, but the difference does matter sometimes.
n
null

You can use git cherry-pick to apply a single commit by itself to your current branch.

Example: git cherry-pick d42c389f


+1 for your former post on cherry picking ( stackoverflow.com/questions/880957/… ). I took the liberty to copy an extract of it in my own answer above.
Probably git cherry-pick d42c or git cherry-pick d42c3 will work. Git is smart. ;)
fatal: bad revision
I just performed this command and it seems to have worked, but when I do git status, it says "nothing to commit, working tree clean". When I refresh the Commits page in my bitbucket web page, it does not show up. But it appears when I execute git log. And I see the modified code. Can someone explain if I have to do any additional steps?
Unfortunately this doesn't answer the question: It doesn't actually create a merge. There's no ancestry pointing to d42c389f. Maybe the OP didn't care about creating a merge per se, but the difference does matter sometimes.
P
Peter Mortensen

Let's try to take an example and understand:

I have a branch, say master, pointing to X , and I have a new branch pointing to Y .

Where Y = branch commits - few commits

Now say for Y branch I have to gap-close the commits between the master branch and the new branch. Below is the procedure we can follow:

Step 1:

git checkout -b local origin/new

where local is the branch name. Any name can be given.

Step 2:

  git merge origin/master --no-ff --stat -v --log=300

Merge the commits from master branch to new branch and also create a merge commit of log message with one-line descriptions from at most actual commits that are being merged.

For more information and parameters about Git merge, please refer to:

git merge --help

Also if you need to merge a specific commit, then you can use:

git cherry-pick <commit-id>

did you change the definition of Y in your 3d sentence? "I have a new branch pointing to Y" vs "Now say for Y branch", sounds like Y used to be a commit and then it became a branch
how i can do that from a certain commit point of a branch which i want to merge
S
Shital Shah

Let's say you want to merge commit e27af03 from branch X to master.

git checkout master
git cherry-pick e27af03
git push

B
Brain2000

I used to cherry pick, but found I had some mysterious issues from time to time. I came across a blog by Raymond Chen, a 25 year veteran at Microsoft, that describes some scenarios where cherry picking can cause issues in certain cases.

One of the rules of thumb is, if you cherry pick from one branch into another, then later merge between those branches, you're likely sooner or later going to experience issues.

Here's a reference to Raymond Chen's blogs on this topic: https://devblogs.microsoft.com/oldnewthing/20180312-00/?p=98215

The only issue I had with Raymond's blog is he did not provide a full working example. So I will attempt to provide one here.

The question above asks how to merge only the commit pointed to by the HEAD in the a-good-feature branch over to master.

Here is how that would be done:

Find the common ancestor between the master and a-good-feature branches. Create a new branch from that ancestor, we'll call this new branch patch. Cherry pick one or more commits into this new patch branch. Merge the patch branch into both the master and a-good-feature branches. The master branch will now contain the commits, and both master and a-good-feature branches will also have a new common ancestor, which will resolve any future issues if further merging is performed later on.

Here is an example of those commands:

git checkout master...a-good-feature  [checkout the common ancestor]
git checkout -b patch
git cherry-pick a-good-feature  [this is not only the branch name, but also the commit we want]
git checkout master
git merge patch
git checkout a-good-feature
git merge -s ours patch

It might be worth noting that the last line that merged into the a-good-feature branch used the "-s ours" merge strategy. The reason for this is because we simply need to create a commit in the a-good-feature branch that points to a new common ancestor, and since the code is already in that branch, we want to make sure there isn't any chance of a merge conflict. This becomes more important if the commit(s) you are merging are not the most recent.

The scenarios and details surrounding partial merges can get pretty deep, so I recommend reading through all 10 parts of Raymond Chen's blog to gain a full understanding of what can go wrong, how to avoid it, and why this works.


I think this is the perfect answer. Thank you. Cherry-pick is very risky. If you cherry-pick a commit X from branchB to branchA, and later if you make commit Y in branchB that changes what commit X did, and you later try to merge branchB to branchA , you will surely get merge conflict.
L
LarsH

The leading answers describe how to apply the changes from a specific commit to the current branch. If that's what you mean by "how to merge," then just use cherry-pick as they suggest.

But if you actually want a merge, i.e. you want a new commit with two parents -- the existing commit on the current branch, and the commit you wanted to apply changes from -- then a cherry-pick will not accomplish that.

Having true merge history may be desirable, for example, if your build process takes advantage of git ancestry to automatically set version strings based on the latest tag (using git describe).

Instead of cherry-pick, you can do an actual git merge --no-commit, and then manually adjust the index to remove any changes you don't want.

Suppose you're on branch A and you want to merge the commit at the tip of branch B:

git checkout A
git merge --no-commit B

Now you're set up to create a commit with two parents, the current tip commits of A and B. However you may have more changes applied than you want, including changes from earlier commits on the B branch. You need to undo these unwanted changes, then commit.

(There may be an easy way to set the state of the working directory and the index back to way it was before the merge, so that you have a clean slate onto which to cherry-pick the commit you wanted in the first place. But I don't know how to achieve that clean slate. git checkout HEAD and git reset HEAD will both remove the merge state, defeating the purpose of this method.)

So manually undo the unwanted changes. For example, you could

git revert --no-commit 012ea56

for each unwanted commit 012ea56.

When you're finished adjusting things, create your commit:

git commit -m "Merge in commit 823749a from B which tweaked the timeout code"

Now you have only the change you wanted, and the ancestry tree shows that you technically merged from B.


When I merge branches, I often go for git merge --no-ff [branch]. Is it possible to apply your solution somehow when using this fast-forward option at merge?
@AntonioRodríguez I haven't tried it, but I don't see any reason why --no-ff would cause any problem with the above approach.
I like this approach (since some express concern about using cherry-pick) but for the fact that revert doesn't result in rewinding commits. It just commits on top of them. Replace git revert --no-commit 012ea56 with git reset --soft 012ea56 and you've moved the pointer back. Then you can just git push without any fuss.
@goldfishalpha I believe that would defeat the purpose of doing the git merge: to have a history that shows two lineages converging. I wonder if you mistook what git reset --soft 012ea56 does: it doesn't rewind 012ea56, but rather sets the branch HEAD to 012ea56 (which would also lose the merge that you've started).
You're right about what revert does, and that it's not as clean as a reset. But I don't think you could rewind commits in a merge, without changing the history of the merged branch.
K
Krishnamoorthy Acharya

If you have committed changes to master branch. Now you want to move that same commit to release-branch. Check the commit id(Eg:xyzabc123) for the commit.

Now try following commands

git checkout release-branch
git cherry-pick xyzabc123
git push origin release-branch

b
bobbogo

In my use case we had a similar need for CI CD. We used git flow with develop and master branches. Developers are free to merge their changes directly to develop or via a pull request from a feature branch. However to master we merge only the stable commits from the develop branch in an automated way via Jenkins.

In this case doing cherry-pick is not a good option. However we create a local-branch from the commit-id then merge that local-branch to master and perform mvn clean verify(we use maven). If success then release production version artifact to nexus using maven release plugin with localCheckout=true option and pushChanges=false. Finally when everything is success then push the changes and tag to origin.

A sample code snippet:

Assuming you are on master if done manually. However on jenkins, when you checkout the repo you will be on the default branch(master if configured).

git pull  // Just to pull any changes.
git branch local-<commitd-id> <commit-id>  // Create a branch from the given commit-id
git merge local-<commit-id>  // Merge that local branch to master.
mvn clean verify   // Verify if the code is build able
mvn <any args> release:clean release:prepare release:perform // Release artifacts
git push origin/master  // Push the local changes performed above to origin.
git push origin <tag>  // Push the tag to origin

This will give you a full control with a fearless merge or conflict hell.

Feel free to advise in case there is any better option.


R
Rishabh Agarwal

We will have to use git cherry-pick <commit-number>

Scenario: I am on a branch called release and I want to add only few changes from master branch to release branch.

Step 1: checkout the branch where you want to add the changes

git checkout release

Step 2: get the commit number of the changes u want to add

for example

git cherry-pick 634af7b56ec

Step 3: git push

Note: Every time your merge there is a separate commit number create. Do not take the commit number for merge that won't work. Instead, the commit number for any regular commit u want to add.


And, in case you want to double check the results, git cherry-pick --no-commit 634af7b56ec . Commit once you're happy.
H
Harikrushna Patel

If you are new to cherrypick try the GitHub desktop version, when you select cherry-pick option, GitHub will ask you to select the branch you want to push a commit.

1)

https://i.stack.imgur.com/wm8cP.png

2)

https://i.stack.imgur.com/X7tx8.png


J
James Killian

I'll suggest an answer. That is to manually change and commit to the target branch by hand via Export, and for using a tool like tortoise, the export can be really an effective way to accomplish this. I know this works quite well in SVN, and so far in some tests from git has had the same success, as long as they are 100% identical there shouldn't be any conflict resolution from future merging of feature branches.

Let me show an example: c:/git/MyProject_Master/ModuleA/ c:/git/MyProject_FeatureA/ModuleA/ Let's suppose we wanted all the files from ModuleA's FeatureA branch to be in the master branch. Assume for a moment this is a huge project and there are other modules, and we know from experience that ModuleA has no dependencies that would cause a compiler or functional issue from the new changes of the feature branch. Select the ModuleA folder and choose Export from tortoise. Then pick ModuleA of master to export it into. Finally, do a file difference check on each file to review the changes, using the diff tool, be sure that all calls out are still compatible. Make sure it compiles, thoroughly test. Commit, push. This solution is a time-tested and effective for svn.


Please try to give less of an impression of a text wall. Formatting code would be helpful and using a few line feeds. Also please try to keep non-answering parts out of your answer, you can put things like "if there are any down sides to this please let me know" in a comment on your own post.
Thanks for taking the feedback. Mentioning that, how and how often you tried your proposed solution is appreciarted info in my opinion. Especially if the environment is not exactly as in the question. I think you could have deleted less. ;-) Asking for feedback on your solution in a comment is fine in my opinion.
Ok I've added that back.