2

Suppose I have branches A and B with a common ancestor, which have diverged. I want to apply some of the changes on branch A to branch B, but without recording that A has been merged into B, so that if I try to do a "real" merge of A into B I still get the remaining differences.

I can do this by:

  • merging without committing
  • creating a patch file from the uncommitted changes
  • aborting the merge
  • applying the patch
  • reverting the changes I don't want for now
  • committing it as a normal (non-merge) commit

but surely there is an easier way?

Context: I have a base branch (say "main") and a feature branch with many changes on it (say "feature"). I want to create a pull request with only some of the changes on the feature branch, so I create a new branch for that ("pr1"). Eventually the rest of the feature branch will probably be merged into "main", so I don't want to record in git history that all of "feature" has been merged into "pr1", as once that's merged it will look as if all of "feature" has been merged into "main" when it hasn't.

Edit: the changes I want in the PR are not neatly contained in a few commits. There have been maybe a hundred commits on the feature branch, including some merges, and I just want to take the current state of it and submit some of it for review.

EM0
  • 5,369
  • 7
  • 51
  • 85
  • You can apply a single or a range of commits to another branch with `git cherry-pick`. This will only work if the commit is directly applicable to the branch. This will however not be treated as a merge, but rather just re-create the changes from the picked commit(s). – Torge Rosendahl Aug 17 '22 at 15:40
  • 1
    Thanks, I know about `cherry-pick` and in simpler cases I use that, but in this case the changes I want are not neatly contained in a just a few commits. (See my comment on matt's answer.) – EM0 Aug 17 '22 at 15:51
  • 2
    Remember that `git merge --squash` makes a single commit whose *content* (file snapshot) matches what `git merge` would produce, but whose *metadata* make it an ordinary single-parent commit. So this gets you a full merge; you can then back out anything you didn't want, and squash the additional commits back into the squash-"merge" commit if you like. – torek Aug 18 '22 at 10:37
  • 1
    @torek Thanks, I'll try that! A simple idea that hadn't occurred to me. – EM0 Aug 18 '22 at 16:19

2 Answers2

1

Assumptions:

  • Some, but not all changes from branch A is to be merged into branch B but without in being a merge commit.
  • No untracked or modified files.

Here is how to create a merge commit without it being recorded as a merge commit:

git checkout -b merge-from-A B
git merge A                         # If conflicts, resolve those and commit.

Do whatever modifications needed to exclude what you do not want to bring in from branch A and amend or create a new commit:

# Alternative 1
git commit --amend

# Alternative 2
git add .
git commit -m "Remove/disable blah blah because blah blah"

Now you have the changes you want from branch A merged into branch merge-from-A. Here is how to bring those into branch B without recording it as a merge:

git chechout B
git diff HEAD merge-from-A | git apply -
git add .

# Alternative 1
git commit -m "Partially merge of blah blah changes from branch A"
git branch -d merge-from-A

# Alternative 2
git commit -m "Partially merge of blah blah changes from branch A" -m "Command run: git diff HEAD merge-from-A | git apply -"
# Keep merge-from-A branch for reference

By my preferences, the alternative 2 choices are the best.

hlovdal
  • 26,565
  • 10
  • 94
  • 165
  • Thanks, this works! The main trick is `git diff HEAD merge-from-A | git apply -`. I can even skip creating `merge-from-A` and do that directly with the branch `A` and then just edit the changes to what I want before committing. – EM0 Aug 18 '22 at 08:53
0

You're probably overthinking this. Why not just reconstruct your existing commits so that they consist of commits you want in pr1 followed by commits you don't want in pr1? Then you just make a branch at the last commit that does go into pr1...

A -- B -- C (main)
     \
      W -- X -- Y -- Z (feature)
         (pr1)

... and push it to form pr1 and now go right on working on feature.

To rearrange how the commits go, use git reset --mixed all the way back to the LCA. See my essay on Three Types of Regret; this is Type 2.

https://stackoverflow.com/a/59675191/341994

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • 1
    There have been many commits on the feature branch and the changes I want are not necessarily contained in a few commits at the start of the history. Lots of code was moved around, stuff got renamed temporarily, etc. I don't need all that history (i.e. I'm happy to squash those commits) - I just want to take the current state of the feature branch and submit a part of the changes. E.g. I added class C and class D, which has a dependency on C. Both changed a lot over many commits, but for now I just want to submit the latest version of C for review (which has no dependency on D, so it's fine). – EM0 Aug 17 '22 at 15:50