-1

I found this and this. But it seems that both these turn all the commits in the development branch into commits on the master branch.

What if I just want to say "what is now the tip of my development branch should become the new tip of the master branch, i.e. the next master branch commit from the last common commit between the two branches".

So basically an unconditional merge, where all conflicts are disregarded in favour of the tip development commit.

Bearing in mind that this is my own project, and no-one else is involved.

Edit

I tried the solution from chepner and got this

(doc_indexer) mike@M17A:/.../doc_indexer$ git cherry-pick my_dev_branch
Auto-merging tests/basic_tests/test_indexing_task.py
CONFLICT (content): Merge conflict in tests/basic_tests/test_indexing_task.py
Auto-merging src/core/visual_log_table_classes.py
Auto-merging src/core/main_window_class.py
CONFLICT (content): Merge conflict in src/core/main_window_class.py
Auto-merging src/core/indexing_task_class.py
CONFLICT (content): Merge conflict in src/core/indexing_task_class.py
error: could not apply d777951... ATG. [... my commit messaage for the tip commit on the dev branch]
hint: after resolving the conflicts, mark the corrected paths
hint: with 'git add <paths>' or 'git rm <paths>'
hint: and commit the result with 'git commit'

... then cancelled the cherry-pick operation.

There's got to be a simple answer to this, and I can't believe I'm the first person to have wanted to do this!

mike rodent
  • 14,126
  • 11
  • 103
  • 157
  • Sounds like you want `git reset`: just make `master` refer to the dev branch head and forget about what `master` used to refer to. – chepner Feb 02 '22 at 21:18
  • Or maybe `git merge -s recursive -X theirs`. – chepner Feb 02 '22 at 21:20
  • @chepner Thanks, but as I say I don't want the commits in the dev branch to become commits on the master branch. I mean, maybe you understood that. If so could you say how I'd do that? – mike rodent Feb 02 '22 at 21:21
  • It's not clear what you are asking. – chepner Feb 02 '22 at 21:22
  • commit C1: master and dev branch common commit. C2, C3, C4: on the dev branch only. Now I want C4 to be the next commit on the master branch, and its previous commit to be C1. It's VERY simple. – mike rodent Feb 02 '22 at 21:23
  • `git cherry-pick`? – chepner Feb 02 '22 at 21:24
  • I'm a low-level gitter, as might be surmised from the question. How might I do that? – mike rodent Feb 02 '22 at 21:25
  • Copying C4 into master with `git cherry-pick` is significantly different from merging your branch into master, as that makes C1 and C2 parts of master, regardless of how you resolve any conflicts. – chepner Feb 02 '22 at 21:28
  • OK in that case that's precisely not what I want. "extended discussions" warning now. If you have another suggestion now you know what I'm trying to do maybe you could just suggest an answer? – mike rodent Feb 02 '22 at 21:30

2 Answers2

1

I think what you're trying to describe is this. You branched development (which I will call dev) from master (which I will call main). Okay, then you continued to work on both branches. But after a while you realized that all your work on main was wrong, that dev was right, and you just want to merge dev into main as if nothing had happened on main since the point of divergence. In other words, starting with this:

A -- B -- C -- D -- E -- F (main)
           \
            X -- Y -- Z (dev)

You want this:

A -- B -- C ---------- MERGE (main)
           \           /
            X -- Y -- Z (dev)

And not only that, you want MERGE to put main in exactly the same state as dev.

There are two parts to the answer. First, you need to erase everything on main back down to the point of divergence:

% git switch main 
% git reset --hard $(git merge-base main dev)

Now do the merge. Git will try to perform a fast-forward, which is not what you want (you have said you don't want main to become identical to dev); so prevent it:

% git merge --no-ff dev

Presto! The last commit on main, the merge commit that you have just created, is identical to the last commit on dev. The reason is that a merge is combination of all changes on both branches since the point of divergence. But there are no changes on main since the point of divergence — because we erased them in the first step. Thus, this merge commit enacts all and only the changes made on dev since the point of divergence, which is exactly what you are asking for (if I understand correctly).

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Note that this solution throws away the "bad" history on `main`, i.e. D, E, and F in my charts. If you would prefer to preserve those commits just for the sake of curiosity or because you like to keep all history where possible, simply make (but do not get onto) a new branch right after the initial `git switch main`. – matt Mar 13 '22 at 16:43
  • Also note that if my guess about what you mean is wrong and there are no D, E, and F, then no harm done, the same answer still works. – matt Mar 13 '22 at 17:02
  • Thanks. The situation is that indeed I do not have D, E or F. I'll try what you suggest but by now have become pessimistic: I believe your solution may, despite everything, result in thousands of irritating ">>>>>" merge marks in the working files. This has happened over and over again with many different solutions I have attempted. – mike rodent Mar 13 '22 at 19:33
  • It is impossible that my solution should generate merge conflicts, as there is nothing on the left side of the merge to conflict with; it is empty. – matt Mar 13 '22 at 19:41
  • Ah, excellent. Yes, you've finally given the answer. No merge marks... and when I run `git log ` for the new commit it shows quite clearly that this has two parents (and also says "Merge"). Incidentally, I ran `git merge -h` and didn't see any option `--no-ff` (maybe it's buried deep in the man pages). Obviously I wouldn't have found this solution even it had been shown ... but I conclude you are a super-expert, git-wise! – mike rodent Mar 13 '22 at 20:06
-1

I think you just want

git checkout master
git cherry-pick dev

where dev is the name of your development branch. This makes the changes introduced by the commit at the tip of dev (and not changes from any of the preceding commits in the same branch) a new commit at the tip of master.

chepner
  • 497,756
  • 71
  • 530
  • 681
  • Thanks. Unfortunately it didn't work: see edit. – mike rodent Feb 12 '22 at 18:55
  • I'm not sure the means it didn't work, only that whatever is at the tip of your dev branch can be cleanly merged with the tip of your master branch. You need to edit the conflicted files to tell Git which parts should be included in the new commit, then continue as instructed. – chepner Feb 12 '22 at 20:36
  • AAAAGH! I don't want to do any "conflict resolution". There is no "conflict resolution". None! Just take the blinking tip of the dev branch and make it the tip of the master. I give up. – mike rodent Feb 12 '22 at 22:48
  • I don't think you quite understand how Git works. You *can't* just graft one commit onto another, as Git doesn't know what the contents of your files are. If there's *any* conflict between how the final file should looks, you need to resolve that yourself. – chepner Feb 14 '22 at 13:14
  • No, sorry, you don't understand the incredibly simple thing I want to achieve. This is a dialogue of the deaf, so not much point continuing really. – mike rodent Feb 14 '22 at 14:49
  • If nobody understands what you actually want, perhaps you aren't clearly describing that. I've mentioned `git reset` to simply make `master` a new name for `dev`; I've mentioned using merge strategy to auto-resolve all conflicts in favor of `dev`; I've mentioned `git cherry-pick` to copy *one* commit from `dev` into master`. Until you provide a concrete example that demonstrates what result you want, I don't see you getting the answer you want. – chepner Feb 14 '22 at 15:14