In a nutshell, merge more and rebase less.

If you can avoid it, don't maintain long-lived actively-developed branches - especially several at once. You are certain to produce conflicts this way, and they will require some amount of manual resolution no matter what SCM you are using.

So my main suggestion is for everyone push their work onto the master much more frequently throughout the development period (whenever it's at a point where all tests are passing and it won't break things for other developers). Anyone working on a long-lived branch can then pull periodically (like, daily) from master and deal with the merge conflicts incrementally, before they've diverged too much.

"Often, what happens in a release is that topic branch 1 relies on commits from topic branch 2. We're not exactly sure how to best handle this, if we start sending patches from topic branch 1 into topic branch 2, will that make it harder for us to merge topic branch 2 into the system test branch?"

No, it won't make it harder. The developer of branch2 can merge from branch1. Git will see in the history that branch2 now contains commits from branch1 and that will help it with later merges.

"Is there a way to move the resolution of the conflicts back into the topic branches? I know this sounds ridiculous but instead of resolving the conflicts in the merge commit, is there a way we could push the resolutions into the proper branches so that the merged version really holds no real state?"

Yes. Instead of checking out master and doing "merge branch1" or "pull branch1", checkout branch1 and do "merge master" or "pull master". (As I suggest above, you should actually do this frequently throughout the development of branch1.) Then when you eventually finish with the branch1 and merge it back into master, all (or almost all) of the merging is already done and you won't have any conflicts.

"Where are the resolutions of the conflict stored right now?"

They're stored in the merge commits - any commit with more than one parent. But if you rebase instead of merging, you lose this information. That's why I say you should rebase less, and instead do "real" merges.

"If I start with a master branch, split off into four topic branches, then merge them back into master, it does a 3-way merge of the end points of each topic branch. If I do a rebase from the merge onto the original master branch, it seems to replay the topic branch lines in sequential order of commits on that line time-sorted by which topic branch was first spun off... is there a way to compress these multiple topic branches and conflict into one patch?"

If you really want to destroy all the branch history and squash everything down into one commit, you could continually "git pull --rebase" from 'master' to 'topic' during development, and then "git merge --squash topic" onto master when development is finished. You'll end up with just one commit.

But I wouldn't do that. If you're doing that, it says that you care more about how the history looks than about your actual development process. Merges may look "ugly" and make it take longer to read through your project history, but that ugliness reflects the reality of concurrent, multi-person development, and merge commits retain accurate history that helps tools like "git merge" work better with fewer conflicts.