EDIT: the question is updated and my original answer was meant for a different question.
Apparently there are:
- two different remotes
- two different teams
- working two different lines of development
(It's not clear whether the two different teams push to the two different remotes, although that would make sense.)
In this case, the two branches are really entirely different lines of development. If you cross them (by merging), you pick up the other branch's history, including commit authors. There is no way around this. If you do merges in both directions, eventually your two IDs will show up in the "other" branch.
The thing you need to understand here is that each commit records (indirectly, but indelibly) the entire history of the development that comes before it, through its parent commit IDs.
A commit-ID is a string (the SHA-1, the thing that looks like
badc0ffee etc in
git log output). There is exactly one unique ID for every commit in the repository. That commit ID is a cryptographic checksum of the contents of the commit: author, email, date, list of parent commit IDs, text message, and source-tree-ID. If you want to change any of these, you must make a new commit—a copy with the change in place—and you will get a new, different ID.
Because each commit stores its parent commit ID(s), you may start with any given commit and find all its parent commits. Using those commits, you may find their parents, and so on. This is what gives you the history of who did what, and when: each of those commits has an author (and committer), date, message, and attached source-tree.
We can draw a graph of these commits. Let's do this with
branchB, and let's assume they are very simple and linear to begin with:
A1 - A2 - A3 <-- branchA
B1 - B2 - B3 <-- branchB
Each ID (
B3) represents some long SHA-1; we don't really care about the values and they are too large to bother with, so let's just pretend we have two-digit SHA-1s.
Now let's assume that you decide to make a commit as user
A <A@A> on
A1 - A2 - A3 - A4 <-- branchA
B1 - B2 - B3 <-- branchB
Your new commit
A4 records, as its parent ID,
A3, and as its author,
A <A@A>. This can never be changed: it's part of the identity
A4. If you try to change it you get a new, different SHA-1.
So now let's copy the change made in
branchB, but under your other name,
B <B@B> and with parent
B3. This is more or less the effect of
git cherry-pick with an amend to change the author, or
git cherry-pick -n followed by a separate
git commit as user-B. You get a new, different commit-ID, which I'll just assume turns out to be
A1 - A2 - A3 - A4 <-- branchA
B1 - B2 - B3 - B4 <-- branchB
Now suppose later you decide to merge (a real merge, not a squash merge, which is not actually a merge at all1). A merge commit is simply one that records two or more parent commit IDs. Let's say this merge goes on
branchA and merges
branchB, which have developed some more since then:
A1 - A2 - A3 - A4 - A5 - M <-- branchA
B1 - B2 - B3 - B4 - B5 <-- branchB
branchB points to your commit
B5 points back to your
B4 (written as user
B <B@B>). The branch-label
branchA now points to merge commit
M, which points to two parents:
B5. By following
B5 back to
A5 back to
A4, anyone with access to commit
M—which they'll have once you push
remoteA—will be able to see that user
B <B@B> did the same thing as user
A <A@A> at pretty much the same time. Furthermore, if commits
B5 did not exist before on
M will cause them to exist, because adding a new commit (whether by
push or by
fetch) brings with it all of its history. Commit
M's history requires
B5, which requires
B4, and so on.
If at some point you merge a commit from
branchB's history, you will also bring in all of that branch's history.
If you never merge the branches, you can keep their history separate, but you make it more difficult for yourself to bring changes together.
1When you use
git merge --squash, git goes through the normal merge mechanisms as usual to build the work-tree, but it does not record the commit(s) being merged, nor make a new commit itself. When you commit the result yourself, it's just a regular (non-merge, single-parent) commit. So it's not actually a merge: it does not get recorded in the commit graph, and git cannot make use of the recorded information to make a future merge easier.
[original answer below]
OK, I see from comments that the goal—I will paraphrase here—is to make commits once, with one author/email pair, but by the time they are to be pushed, have those commits get copied to new (different) commits with the same trees but a different author/email pair.
Commits with, e.g., author/email
A <A@A> are then pushed to
remoteA, and (the same) commits but with author/email
B <B@B> are pushed to
remoteB do not share commits and can get out of sync (if, e.g., one is down when you go to push) and it's not easy to have the two remotes talk to each other to resync. (It's not impossible; they'd just have to do the same filtering you'd be doing here.)
[If this is not the actual goal (e.g., if you simply need to authenticate to
remoteB as a different user, but can push commits containing
A <A@A> as their author strings) then the rest of this answer is pointless.]
Overall I don't think this is a very good plan, but it can be done. Here's the outline, with two different methods that have various pitfalls:
Make commit-copies at commit-time (with, e.g., a post-commit hook). This assumes you commit exactly once and never have to amend or rebase.
Make commit-copies at publication time, e.g., just before
git push (write a script that vets commits, copies them as needed, and then does the two pushes, for instance). This is probably the superior method, although it makes pushing slower. In particular it lets you replace commits that have never been sent upstream: you can still use
git commit --amend and
In both cases you may run into problems if one or both remotes have new commits that you must incorporate before
pushing. For instance, suppose
remoteA (which will acquire three new commits written by
A <A@A>) is all in sync now and you push the three commits. They arrive safely and all is fine. But now the copies you have made, listed as written by
B <B@B>, are set to be pushed to
remoteB; but it turns out that
remoteB has acquired some new commits on its own, and the copies themselves need to be rebased. Moreover, you might wipe out "their" changes (whoever "they" are that made changes that wound up on
remoteB). This will happen with the tree-reset method I describe below.
Anyway, to make a copy of a commit, here's a way to cheat. Let's start by assuming you have a branch named
devel-for-A, in which you make commits with author set to
A and email set to
A@A; and you have a parallel branch
devel-for-B, in which you copy those commits but with the other author and email.
$ git checkout devel-for-A
... hack away ...
$ git commit -m 'new stuff'
Now to copy these to
devel-for-B, we simply substitute in the correct
HEAD during the commit, then swap it back:
$ git symbolic-ref HEAD refs/heads/devel-for-B
$ git commit -C devel-for-A --reset-author --author='B <B@B>'
$ git symbolic-ref HEAD refs/heads/devel-for-A
Note that you can use
-C to copy the message from the previous commit, but you must then also use
--reset-author. Here I've made use of the fact that
devel-for-A names the commit just made. This will not be the case if you do the copies later, en-masse, just before the push. In that case the simple (but not very efficient) method is to use
git rm -r .; git checkout $rev -- . to import the tree from the other branch to the current one (so you'd get on
devel-for-B and then import commits from
devel-for-A). See the next few paragraphs.
If you're copying commits just before push, you would want to use
git rev-list to generate the IDs to be copied (this is
$rev above but you need some shell script code to use it). To know which commits to copy, you may need to add extra references to keep track of the last successful ID pushed to
remoteB (assuming you're doing the copying from the
devel-for-A branch into the
devel-for-B branch). (I have not thought through all the details on this one. The remote-branch label is updated automatically so you know which commits made it across, but you may not know which original
A-branch commits they map to.)
If you're copying commits en masse, doing it with
git checkout is overkill: you could read the commits into the index. Basically, you need to do what
filter-branch does (so see that script), but instead of rewriting the existing branch label (
devel-for-A) afterward, you (re)write the