4

I want to overwrite all the content in a development branch with another one (namely, master), but I don't want to miss the older commits on the dev branch.

This is what I want:

what I want

I tried both to merge with ours strategy and by doing an hard reset but the dev branch keeps losing the older commits.

What I'm trying to obtain is a commit that has zero diff between blue and red, and the dropped code while diffing blue with the first black one.


Just to share an overview of the solutions tried until now. By doing this:

git checkout master
git merge -s ours dev
git checkout dev
git merge master

I obtain this (dev gets all the history from master)

merge

By doing this

git checkout dev
git reset --hard master

I obtain this (if I push develop i will lose all the black commits):

reset

If i do this:

git checkout dev
git merge $(git commit-tree -p dev -m "Align master > dev" master^{tree})

I obtain this (almost perfect, yet I would be difficult to recall the exact moment when I did the trick):

enter image description here

freshdevelop
  • 155
  • 11
  • just curious why putting past history on the dev branch this way? why not rebase? – Daniel A. White May 20 '20 at 14:19
  • 1
    he want to keep old history. rebase would discard it. – Marek R May 20 '20 at 14:20
  • Rebase makes a lot of conflicts arise in the process (and having a whole lot of commits, I'm having a ton of conflicts. This is simply not practical). I imagined not to have any conflict because of a complete, blindfolded overwrite of everything in the dev branch. – freshdevelop May 20 '20 at 14:55
  • If you use the ours strategy (`merge -s ours`) no history and no commits will be lost. All changes will be "lost" (i.e. overwritten) though. But that is exactly what you are asking. See also: [How do I 'overwrite', rather than 'merge', a branch on another branch in Git?](https://stackoverflow.com/q/4624357/112968) – knittl May 20 '20 at 15:00
  • @knittl I tried both the -s ours and the -X theirs strategies (checking out respectively on the target and the source branches), but after those I still have differences between develop and master (namely, I have some never-deployed data on develop that I want to reset, but since merge is a non-destructive procedure this stuff continues to exist on develop). – freshdevelop May 20 '20 at 15:47
  • @freshdevelop I would understand this for `-X` which is a strategy option for how to deal with conflicts. `-s` takes everything from one branch and nothing from the other. Should we continue this [in the chat](https://chat.stackoverflow.com/rooms/214272/https-stackoverflow-com-questions-61914439-git-overwrite-branch-but-keep-histor)? – knittl May 20 '20 at 15:49
  • What you want is the equivalent of `git merge -s theirs`, which does not exist—but see https://stackoverflow.com/a/46741538/1256452 and https://stackoverflow.com/q/1910444/1256452 – torek May 20 '20 at 19:19

2 Answers2

5

This can go like this:

git checkout -f develop # go to develop branch
git merge --no-commit master
git checkout master -- . # this checkouts code from master and doesn't switch branch
# you are still in merge process on branch develop
git commit

Note that tailing -- . changes behavior of git checkout.

Marek R
  • 32,568
  • 6
  • 55
  • 140
  • Works well for keeping the old commits but then running `git diff dev..master` gives me non-empty results. – freshdevelop May 20 '20 at 14:28
  • did you run this commands in root directory of repository? This `.` means current directory. If you were in sub-directory then not everything from `master` will be checkout. – Marek R May 20 '20 at 14:31
  • Yes, I confirm that I'm running bash from the base folder. Still, diffing gives me results on files that does not seem to be changed at all in the "blue" commit. – freshdevelop May 20 '20 at 14:43
  • That is interesting, that there is a problem with deleted files. – Marek R May 20 '20 at 20:21
  • 1
    Sorry, I accidentally deleted the previous comment about file removal. What I was saying, third instruction leaves behind file removals, that are not reported from master to develop. Doing `git rm $(git diff --name-only master -- .)` seems to fix the problem. – freshdevelop May 21 '20 at 07:07
2

If I understand your needs correctly, you want to make a new commit on dev and the commit makes the content of dev exactly the same with master. If so,

git checkout dev
git merge $(git commit-tree -p dev -m "foo" master^{tree})

git commit-tree creates a commit from master head's tree, so that the new commit has the same content with master. The command returns the hash of the commit, which can be passed to git merge.

-p dev specifies dev head as the new commit's parent.

-m "foo" uses foo as the new commit's message. You can use literally foo, a random string, because in the end you can use git commit --amend to specify a formal message.

master^{tree} means the hash of the tree object of master head.

In this case git merge has the same effect with git reset --hard.

ElpieKay
  • 27,194
  • 6
  • 32
  • 53
  • Hey, this seems to work! Thanks! Only thing is, what I'm getting is a new commit on develop that equals master (so diff is empty as expected) but there's not an explicit link between the branches (as I can see in my gui). – freshdevelop May 20 '20 at 15:09