Skip to main content
address comment
Source Link
VonC
  • 1.4m
  • 569
  • 4.8k
  • 5.7k

When it comes to a range of commits, cherry-picking is was not practicalimpractical.

In the "cherry-pick A..B" form, A should be older than B.
If they're the wrong order, the command will silently fail.

If you want to pick the range B through D (including B) that would be B^B~..D (instead of B..D).
See "Git create branch from range of previous commits?" asfor an illustration.

Note: as of Git 2.9.x/2.10 (Q3 2016), you can cherry-pick a range of commitcommits directly on an orphan branch (empty head): see "How to make an existing branch an orphan in gitGit".

A rebase --onto would be better, where you replay the given range of commitcommits on top of your integration branch, as Charles Bailey described here.
(also, look for "Here is how you would transplant a topic branch based on one branch to another" in the git rebase man page, to see a practical example of git rebase --onto)

  • after the parent of first_SHA-1_of_working_branch_range (hence the ~1): the first commit you want to replay
  • up to "integration" (which points to the last commit you wantintend to replay, from the working branch)

If you want to use a patch approach then "git format-patch|git am" and "git cherry" are your options.
Currently, git cherry-pick accepts only a single commit, but if you want to pick the range B through D that would be B^..D (actually B~..D, see below) in gitGit lingo, so:

git rev-list --reverse --topo-order B^B~..D | while read rev do git cherry-pick $rev || break done 

But anyway, when you need to "replay" a range of commits, the word "replay" should push you to use the "rebase" feature of Git.


pridmorej objects in the comments:

WARNING: don't be fooled by the above suggestion of using carat (^) to make the range inclusive!

This does not include CommitId1 if, for instance, the parent of CommitId1 is a merge commit:git cherry-pick CommitId1^..CommitId99.
In that case, cherry-pick still starts from CommitId2 - no idea why, but that's the behavior I've experienced.

I did, however, discover that using tilde (~) works as expected, even when the parent of CommitId1 is a merge commit: git cherry-pick CommitId1~..CommitId99.

True: that highlights an important nuance in using Git's cherry-picking command with commit ranges, particularly when dealing with merge commits.

In Git, ^ and ~ have specific meanings when used with commit references:

  • ^ refers to the parent of a commit, and when used in a range, it can lead to unexpected results, especially with merge commits.
  • ~, on the other hand, is used to denote the first parent of a commit in a linear history, which makes it more predictable in the context of cherry-picking a range.

When cherry-picking a range of commits, especially in scenarios involving merge commits, prefer using ~ to make sure the range includes the intended commits.

So regarding git cherry-pick CommitId1^..CommitId99: When specifying a range CommitId1^..CommitId99, Git interprets this as "start from the parent of CommitId1 and include commits up to CommitId99".

  • If CommitId1 is a regular commit, its only parent (say CommitId0) becomes the start of the range, effectively excluding CommitId1 itself.
  • If CommitId1 is a merge commit, CommitId1^ still points to its first parent. That can be particularly confusing because merge commits by their nature merge two lines of development, and the first parent might not be intuitively the "previous" commit in a linear sense.

In non-linear histories involving merge commits, the first parent of a merge commit might not be the direct predecessor in the same branch.

The tilde (~) notation, when used as in CommitId1~..CommitId99, effectively means "include CommitId1 and go back one commit from there", which in most cases will include CommitId1 in the range, as intended.

Consider the following commit history, where M represents a merge commit, and each letter represents a different commit:

A---B---C-------D---E--CommitId99 <- master \ / X---Y---M <- feature (CommitId1 is M) 
  • A, B, C, D, E are commits on the master branch.
  • X, Y are commits on a feature branch.
  • M is a merge commit on the feature branch, merging changes from master into feature. Here, M is CommitId1.

When you run the command:

git cherry-pick CommitId1^..CommitId99 
  • CommitId1 is M.
  • CommitId1^ refers to the first parent of M.
  • In this case, the first parent of M is D because merge commits list their parents in the order they were merged.

So, the range CommitId1^..CommitId99 translates to D..CommitId99. M (CommitId1) is excluded!

But if you use git cherry-pick CommitId1~..CommitId99, when used in the context of a range, like CommitId1~..CommitId99, the interpretation is "start from the commit right before CommitId1 and include up to CommitId99."

So CommitId1~ refers to the commit right before M in the feature branch, which is Y (since M was created on the feature branch, by a merge from master to feature).
The range CommitId1~..CommitId99 translates to Y..CommitId99.

Using ~ in the range with git cherry-pick effectively shifts the start of the range to the commit right before CommitId1, thereby including CommitId1 in the cherry-picked range. That behavior is particularly useful when you want to include merge commits in your cherry-picking operation.

When it comes to a range of commits, cherry-picking is was not practical.

In the "cherry-pick A..B" form, A should be older than B.
If they're the wrong order the command will silently fail.

If you want to pick the range B through D (including B) that would be B^..D (instead of B..D).
See "Git create branch from range of previous commits?" as an illustration.

Note: as of Git 2.9.x/2.10 (Q3 2016), you can cherry-pick a range of commit directly on an orphan branch (empty head): see "How to make existing branch an orphan in git".

A rebase --onto would be better, where you replay the given range of commit on top of your integration branch, as Charles Bailey described here.
(also, look for "Here is how you would transplant a topic branch based on one branch to another" in the git rebase man page, to see a practical example of git rebase --onto)

  • after the parent of first_SHA-1_of_working_branch_range (hence the ~1): the first commit you want to replay
  • up to "integration" (which points to the last commit you want to replay, from the working branch)

If you want to use a patch approach then "git format-patch|git am" and "git cherry" are your options.
Currently, git cherry-pick accepts only a single commit, but if you want to pick the range B through D that would be B^..D in git lingo, so

git rev-list --reverse --topo-order B^..D | while read rev do git cherry-pick $rev || break done 

But anyway, when you need to "replay" a range of commits, the word "replay" should push you to use the "rebase" feature of Git.

When it comes to a range of commits, cherry-picking is was impractical.

In the "cherry-pick A..B" form, A should be older than B.
If they're the wrong order, the command will silently fail.

If you want to pick the range B through D (including B) that would be B~..D (instead of B..D).
See "Git create branch from range of previous commits?" for an illustration.

Note: as of Git 2.9.x/2.10 (Q3 2016), you can cherry-pick a range of commits directly on an orphan branch (empty head): see "How to make an existing branch an orphan in Git".

A rebase --onto would be better, where you replay the given range of commits on top of your integration branch, as Charles Bailey described here.
(also, look for "Here is how you would transplant a topic branch based on one branch to another" in the git rebase man page, to see a practical example of git rebase --onto)

  • after the parent of first_SHA-1_of_working_branch_range (hence the ~1): the first commit you want to replay
  • up to "integration" (which points to the last commit you intend to replay, from the working branch)

If you want to use a patch approach then "git format-patch|git am" and "git cherry" are your options.
Currently, git cherry-pick accepts only a single commit, but if you want to pick the range B through D that would be B^..D (actually B~..D, see below) in Git lingo, so:

git rev-list --reverse --topo-order B~..D | while read rev do git cherry-pick $rev || break done 

But anyway, when you need to "replay" a range of commits, the word "replay" should push you to use the "rebase" feature of Git.


pridmorej objects in the comments:

WARNING: don't be fooled by the above suggestion of using carat (^) to make the range inclusive!

This does not include CommitId1 if, for instance, the parent of CommitId1 is a merge commit:git cherry-pick CommitId1^..CommitId99.
In that case, cherry-pick still starts from CommitId2 - no idea why, but that's the behavior I've experienced.

I did, however, discover that using tilde (~) works as expected, even when the parent of CommitId1 is a merge commit: git cherry-pick CommitId1~..CommitId99.

True: that highlights an important nuance in using Git's cherry-picking command with commit ranges, particularly when dealing with merge commits.

In Git, ^ and ~ have specific meanings when used with commit references:

  • ^ refers to the parent of a commit, and when used in a range, it can lead to unexpected results, especially with merge commits.
  • ~, on the other hand, is used to denote the first parent of a commit in a linear history, which makes it more predictable in the context of cherry-picking a range.

When cherry-picking a range of commits, especially in scenarios involving merge commits, prefer using ~ to make sure the range includes the intended commits.

So regarding git cherry-pick CommitId1^..CommitId99: When specifying a range CommitId1^..CommitId99, Git interprets this as "start from the parent of CommitId1 and include commits up to CommitId99".

  • If CommitId1 is a regular commit, its only parent (say CommitId0) becomes the start of the range, effectively excluding CommitId1 itself.
  • If CommitId1 is a merge commit, CommitId1^ still points to its first parent. That can be particularly confusing because merge commits by their nature merge two lines of development, and the first parent might not be intuitively the "previous" commit in a linear sense.

In non-linear histories involving merge commits, the first parent of a merge commit might not be the direct predecessor in the same branch.

The tilde (~) notation, when used as in CommitId1~..CommitId99, effectively means "include CommitId1 and go back one commit from there", which in most cases will include CommitId1 in the range, as intended.

Consider the following commit history, where M represents a merge commit, and each letter represents a different commit:

A---B---C-------D---E--CommitId99 <- master \ / X---Y---M <- feature (CommitId1 is M) 
  • A, B, C, D, E are commits on the master branch.
  • X, Y are commits on a feature branch.
  • M is a merge commit on the feature branch, merging changes from master into feature. Here, M is CommitId1.

When you run the command:

git cherry-pick CommitId1^..CommitId99 
  • CommitId1 is M.
  • CommitId1^ refers to the first parent of M.
  • In this case, the first parent of M is D because merge commits list their parents in the order they were merged.

So, the range CommitId1^..CommitId99 translates to D..CommitId99. M (CommitId1) is excluded!

But if you use git cherry-pick CommitId1~..CommitId99, when used in the context of a range, like CommitId1~..CommitId99, the interpretation is "start from the commit right before CommitId1 and include up to CommitId99."

So CommitId1~ refers to the commit right before M in the feature branch, which is Y (since M was created on the feature branch, by a merge from master to feature).
The range CommitId1~..CommitId99 translates to Y..CommitId99.

Using ~ in the range with git cherry-pick effectively shifts the start of the range to the commit right before CommitId1, thereby including CommitId1 in the cherry-picked range. That behavior is particularly useful when you want to include merge commits in your cherry-picking operation.

Clarified text
Source Link
Top-Master
  • 9k
  • 6
  • 52
  • 97

If you want to pick the range B through D (inclusiveincluding B) that would be B^..D (instead of B..D).
See "Git create branch from range of previous commits?" as an illustration.

If you want to pick the range B through D (inclusive) that would be B^..D.
See "Git create branch from range of previous commits?" as an illustration.

If you want to pick the range B through D (including B) that would be B^..D (instead of B..D).
See "Git create branch from range of previous commits?" as an illustration.

restore link
Source Link
VonC
  • 1.4m
  • 569
  • 4.8k
  • 5.7k

A pure "cherry-pick" solution is discussed herediscussed here, and would involve something like:

A pure "cherry-pick" solution is discussed here, and would involve something like:

A pure "cherry-pick" solution is discussed here, and would involve something like:

Ficks typoe
Source Link
Loading
replaced http://stackoverflow.com/ with https://stackoverflow.com/
Source Link
URL Rewriter Bot
URL Rewriter Bot
Loading
add orphan
Source Link
VonC
  • 1.4m
  • 569
  • 4.8k
  • 5.7k
Loading
add caveat
Source Link
VonC
  • 1.4m
  • 569
  • 4.8k
  • 5.7k
Loading
add range cherry-picking
Source Link
VonC
  • 1.4m
  • 569
  • 4.8k
  • 5.7k
Loading
http://www.kernel.org/pub/software/scm/git/docs/git-rebase.html was remapped to http://git-scm.com/docs/git-rebase by Sam Saffron (17174)
Source Link
Loading
add cherry-pick evolution
Source Link
VonC
  • 1.4m
  • 569
  • 4.8k
  • 5.7k
Loading
fix wrong word
Source Link
noah
  • 21.6k
  • 17
  • 69
  • 88
Loading
fix typos, add parent commit
Source Link
VonC
  • 1.4m
  • 569
  • 4.8k
  • 5.7k
Loading
add cherry-pick solution; added 434 characters in body
Source Link
VonC
  • 1.4m
  • 569
  • 4.8k
  • 5.7k
Loading
Source Link
VonC
  • 1.4m
  • 569
  • 4.8k
  • 5.7k
Loading