2

So imagine an existing code base with files {A, B, C, D, E}. I clone the repo, create a branch, and issue a series of changes

Commit 1 changes files {A, B}
Commit 2 changes files {C, D}
Commit 3 changes files {D, E}

In this example, I need to merge to the parent the files that changed in commit 2, but without any of the changes in Commit 1.

It is my understanding that each commit contains snapshots of the entire code base, as illustrated here: enter image description here source

So commit 2 has the changes to files C and D, but it also has the changes to file A and B from commit 1, which should not be merged.

So, how would I merge up the versions of C and D from Commit 2, without changing the parent's version of A and B?

Frank Thomas
  • 2,434
  • 1
  • 17
  • 28
  • Not dupe. this isn;t about merging from a specific commit, its about mergeing only the files that changed in that commit, excluding all changes in prior commits. – Frank Thomas Mar 07 '18 at 17:10
  • 1
    As far as I know you can't target the exact files and only include some files from a commit. But if you want to merge only a specific commit, but not other commits, from a branch into another, you would use the cherry pick feature. Cherry picking can be messy, though. Use it with caution. But it does what you're looking for. – adprocas Mar 07 '18 at 17:11
  • When you say `In this example, I need to merge to the parent the files that changed in commit 2, but without any of the changes in Commit 1.`, that's what the cherry pick would do. Take only changes from commit 2, and not commit one. – adprocas Mar 07 '18 at 17:13
  • @adpro, Cherry picking merges entire commits, and per the image above the changes to A and B from Commit 1 are present in Commit 2, so if I merged commit 2, it would change the parents copy of A and B, right? – Frank Thomas Mar 07 '18 at 17:13
  • So you want to target specific files in a commit only? Commit only changes to specific files you specify from a commit only? – adprocas Mar 07 '18 at 17:14
  • exactly. In TFS or SVN, I'd just merge the pair of files. I don't mind that git merges commits, but since the entire code base in in every commit, and merges just blindly diff source and target, I don't know how to get more precision. – Frank Thomas Mar 07 '18 at 17:20
  • You can commit single files by specifying what files you want to commit. You don't have to commit all at once. – adprocas Mar 07 '18 at 17:22
  • But, as far as I know you are not able to merge only single files from a commit. I believe this is the same in SVN. If I merge I have to merge up to an including all previous commits, don't I? I'll have to test. I don't do a lot of merging in SVN, but I think you're talking about committing single files. You can do this in git. https://stackoverflow.com/questions/8795097/how-to-git-commit-a-single-file-directory – adprocas Mar 07 '18 at 17:24
  • its been 10 yeas since I used SVN so perhaps I misremember, but either way, my concern is how to up C and D without upping A and B. – Frank Thomas Mar 07 '18 at 17:27
  • 1
    So, files changed in commit 2? Does cherry pick not work for that? I'm confused. Why can't you just cherry pick commit 2? What does cherry pick do that you don't want it to do? – adprocas Mar 07 '18 at 17:34
  • because the parent has a verion of A and B from before Commit 1 (lets call it commit 0). I need to update the parents copy of C and D, but must not update the parents copy of A and B. when I merge, git will see that the diff between the parent and the current branch at commit to are files {A, B, C, D}. I need to merge C and D, but not A and B. – Frank Thomas Mar 07 '18 at 17:38
  • duplicate: https://stackoverflow.com/questions/6372044/how-do-i-merge-a-specific-commit-from-one-branch-into-another-in-git – adprocas Mar 07 '18 at 18:05
  • Possible duplicate of [How to merge a specific commit in Git](https://stackoverflow.com/questions/881092/how-to-merge-a-specific-commit-in-git) – Rory O'Kane Mar 07 '18 at 22:29

2 Answers2

3

This is what git cherry-pick is for.

As I understand it, you want a new branch with only commit 2 on top of master, starting from this state:

o 8021  commit 3 that changes D and E  [feature-branch (HEAD)]
|
o bb6e  commit 2 that changes C and D
|
o f8a2  commit 1 that changes A and B
|
o 218a  commit 0 the initial state of the repository  [master]

First you should create a new branch just-commit-2 on top of master and check it out:

git checkout master
git checkout -b just-commit-2

Then cherry-pick commit 2:

git cherry-pick bb6e

You will end up with this state:

o 12c9  commit 2 that changes C and D  [just-commit-2 (HEAD)]
|
| o 8021  commit 3 that changes D and E  [feature-branch]
| |
| o bb6e  commit 2 that changes C and D
| |
| o f8a2  commit 1 that changes A and B
|/
o 218a  commit 0 the initial state of the repository  [master]

If you look at the state of your working tree at just-commit-2, files C and D will be the only files different from the files in master.

Rory O'Kane
  • 29,210
  • 11
  • 96
  • 131
  • So you are saying that when a commit is merged, even though it contains files that were changed in prior commits, that those changes are not merged? then how can you merge from the branch tip commit, and merge changes from all the commits on that branch since the parents last shared commit? or are you saying that Cherry-picking is more special than just merging specific commits, and specially ignores all files that are different except those CHANGED in the specific commit, which is differant from default merge behavior? – Frank Thomas Mar 07 '18 at 17:51
  • @FrankThomas, I think this is the case, so I was wrong again. So, this is a duplicate. I will mark it as such again. I would suggest testing it out with a different set branches first that you can discard. Let me know if it doesn't work as you expect. – adprocas Mar 07 '18 at 17:55
  • @FrankThomas I'm saying when a commit is *cherry-picked*, then changes in prior commits are not merged. When you *merge*, which is done with `git merge`, then changes in prior commits *will* be included. – Rory O'Kane Mar 07 '18 at 17:55
  • The answer may be the same, but the question definitely isn't. thats a lot of why I had to ask. – Frank Thomas Mar 07 '18 at 17:57
  • I will have to find the original question that I marked as a duplicate. That question was the same. – adprocas Mar 07 '18 at 17:59
  • This was it - https://stackoverflow.com/questions/6372044/how-do-i-merge-a-specific-commit-from-one-branch-into-another-in-git – adprocas Mar 07 '18 at 18:05
  • @adpro No, it was the similarly-titled https://stackoverflow.com/questions/881092/how-to-merge-a-specific-commit-in-git, which seems closer to this question. – Rory O'Kane Mar 07 '18 at 18:06
  • @RoryO'Kane, I think you're correct. Either way, the question shouldn't be answered and it should be marked as duplicate. Since I retracted my duplicate request I can not mark it as duplicate again. – adprocas Mar 07 '18 at 18:07
  • @RoryO'Kane, The accepted answer on the https://stackoverflow.com/questions/881092/how-to-merge-a-specific-commit-in-git question in fact specificly states that a commit contains all the history for commits before it when describing cherry picking a commit. thats exactly what I need to avoid. ignoring all history except the changes in that specific commit. You are probably right, but the distinction between the entire content of a commit and the changes that were first recorded in that commit seems to be the point on which I am unsure. – Frank Thomas Mar 07 '18 at 18:08
  • @FrankThomas, why not test it out and find out yourself? – adprocas Mar 07 '18 at 18:09
  • Also, if you read closer he's talking about commits in general. Cherry picking takes that commit, looks at those changes, then applies a patch (probably not really, but essentially). So, it loses the history, and that is mentioned. – adprocas Mar 07 '18 at 18:11
  • I already tested my answer in a repo and confirmed that it doesn’t include changes from other commits. To reproduce, in a new directory, `git init`, `touch a.txt b.txt c.txt b.txt e.txt`, `git add -A && git commit -m "Initial commit"`, `git checkout -b feature-branch`, `echo content > a.txt`, `echo content > b.txt`, `git add -A && git commit -m "commit 1 a/b"`, etc. until you create commit 3. – Rory O'Kane Mar 07 '18 at 18:16
  • @RoryO'Kane, thanks for the update. Any chance you could mark this as a duplicate? That was my original intent, and I did mention cherry-pick as the solution in the comments. – adprocas Mar 07 '18 at 18:53
1

One way would be to create a new branch out of your branch which has all 3 commits. Use git rebase to drop unwanted commits. Then checkout to parent branch and the use git merge new branch name

slashpai
  • 1,141
  • 7
  • 12
  • I'm still coming to terms with rebase, but I'm thinking that you are saying that you would replay the commits 1,2,3 with interactive rebase, and use the `d` option to drop commits 1 and 3. is that more or less it? so how would I go about doing it if the commits are already on the server, in light of advise to never rebase/rewrite public history? is that why we are merging to a new branch? – Frank Thomas Mar 07 '18 at 17:25
  • You are not rewriting history on server. Currently you have made new commits in ur local branch only and u want only one commit to be updated to main branch. So rebase after checking out new branch would work like I mentioned above – slashpai Mar 08 '18 at 04:42
  • unfourtunatly, that isn;t the case for us. This is a Salesforce solution using deployment tools to pull from the instance to version control, so it can only target online repositories, so all initial commits of code are already on the server. man I hate salesforce. – Frank Thomas Mar 08 '18 at 13:40
  • So what I understand is you clonned repo to local, created a branch then made those 3 commits. Since you haven't pushed commits to remote yet its not yet there. So if u create a new branch out of current branch, drop unwanted commits and then merge it later to main branch locally and then push to remote – slashpai Mar 08 '18 at 13:44
  • I was trying to ask the question more generically so as to not get distracted by the nature of the specific case, but each of the three commits are directly to the origin from a salesforce deployment tool called Gearset. I `pull` them down to do merges and whatnot, and `push` them back to the server so GS can see them for deployment to another sandbox or prod. obviously sub optimal. v – Frank Thomas Mar 08 '18 at 13:53