How do I 'overwrite', rather than 'merge', a branch on another branch in Git?

Solution 1:

You can use the 'ours' merge strategy (this links to octopus merge, you have to scroll down to next option; deeplinking to the correct section is not possible):

$ git checkout staging
$ git merge -s ours email # Merge branches, but use our (=staging) branch head
$ git checkout email
$ git merge staging

EDIT 2020-07-30:

I thought a bit more about this question and possible solutions. If you absolutely require the merge parents in the correct order, need to perform this action with a single command line invocation, and don't mind running plumbing commands, you can do the following:

$ git checkout A
$ git merge --ff-only $(git commit-tree -m "Throw away branch 'A'" -p A -p B B^{tree})

This basically acts like the (non-existent) merge -s theirs strategy. You can find the resulting history in the plumbing branch of the demo repository

Not very readable and not as easy to remember compared to the -s ours switch, but it does the job. The resulting tree is again the same as branch B:

$ git rev-parse A^{tree} B^{tree} HEAD^{tree}
3859ea064e85b2291d189e798bfa1bff87f51f3e
0389f8f2a3e560b639d82597a7bc5489a4c96d44
0389f8f2a3e560b639d82597a7bc5489a4c96d44

EDIT 2020-07-29:

There seems to be a lot of confusion as to what the difference between -s ours and -X ours (the latter being equivalent to -s recursive --strategy-option ours) is. Here's a small example to show the two results from using these two methods. I also recommend reading the question and answers of (Git Merging) When to use 'ours' strategy, 'ours' option and 'theirs' option?

First, setup a repository with 2 branches and 3 commits (1 base commit, and 1 commit per branch). You can find the sample repository on GitHub

$ git init
$ echo 'original' | tee file1 file2 file3
$ git commit -m 'initial commit'
$ git branch A
$ git branch B
$ git checkout A
$ echo 'A' > file1
$ git commit -m 'change on branch A' file1
$ git checkout B
$ echo 'B' > file2
$ git commit -m 'change on branch B' file2

Now, let's try the strategy option (doesn't really matter if we use theirs or ours for this explanation):

$ git merge -X ours A
$ cat file*
A
B
original

We end up with a merge of both branches' contents (branch "strategy-option" in the sample repo). Compare that to using the merge strategy (re-init your repository or reset branch, before executing the next steps):

$ git merge -s ours A
$ cat file*
original
B
original

The result is quite different (branch "merge-strategy" in the sample repo). With the strategy option, we get a merge result of both branches, with the strategy we throw away any changes which happened in the other branch.

You will also notice that the commit created by the merge-strategy in fact points to the exact same tree than the latest commit of "our" branch, while the strategy-option created a new, previously unseen tree:

$ git rev-parse A^{tree} B^{tree} merge-strategy^{tree} strategy-option^{tree}
3859ea064e85b2291d189e798bfa1bff87f51f3e
0389f8f2a3e560b639d82597a7bc5489a4c96d44
0389f8f2a3e560b639d82597a7bc5489a4c96d44
5b09d34a37a183723b409d25268c8cb4d073206e

OP indeed asked for "I no longer need the old changes in […] branch" and "So I just want to dump all the contents of [A] into [B]", which is not possible to do with a strategy option. Using the 'ours' merge strategy is one possibility of many, but likely the easiest (other possibilities include using low level commands of Git such as write-tree and commit-tree).

Solution 2:

If you just want the two branches 'email' and 'staging' to be the same, you can tag the 'email' branch, then reset the 'email' branch to the 'staging' one:

$ git checkout email
$ git tag old-email-branch
$ git reset --hard staging

You can also rebase the 'staging' branch on the 'email' branch. But the result will contains the modification of the two branches.