4

I did several commits (not pushed) using source tree with the wrong mail address.

To correct this error I did some research and found this script to edit the commit with a good mail. The thing is, when I first pulled the project from git there were already over 200 commits from several users.

When I used the script it reverted my mail addresses correctly but the others were destroyed :

ex : a.my@mail.com became a.my@5030863e-2e11-0d4c-b7c1-a084646f5798

Do you have any idea how I can manage this problem ?

#!/bin/sh

git filter-branch -f --env-filter '

OLD_EMAIL="a.bbbb@5030863e-2e11-0d4c-b7c1-a084646f5798"
CORRECT_NAME="a.bbbb"
CORRECT_EMAIL="a.bbbb@mail.com"

if [ "$GIT_COMMITTER_EMAIL" = "$OLD_EMAIL" ]
then
    export GIT_COMMITTER_NAME="$CORRECT_NAME"
    export GIT_COMMITTER_EMAIL="$CORRECT_EMAIL"
fi
if [ "$GIT_AUTHOR_EMAIL" = "$OLD_EMAIL" ]
then
    export GIT_AUTHOR_NAME="$CORRECT_NAME"
    export GIT_AUTHOR_EMAIL="$CORRECT_EMAIL"
fi
' --tag-name-filter cat -- --branches --tags

EDIT1 :

I have used the script on the other mails adresses

it corrected everything as it was before but sourcetree tells me that master is 248up/248down :

enter image description here

How could i get ride of this status ?

EDIT2:

As suggested i used

git branch -f master origin/master

it corrected the status of the repo (248up/248down disappeared)

but i still have 2 times the history in sourcetree, in purple we can see the last commit in distant repo (with the wrong mails i modified), starting from the blue part there is the correct history with at the end my local commits (develop branch and features with good mails) :

enter image description here

BJ Myers
  • 6,617
  • 6
  • 34
  • 50
An-droid
  • 6,433
  • 9
  • 48
  • 93
  • The script above changed the author information of every commit? You can limit the refs that `filter-branch` operates on with revision selectors. Perhaps you should do that? – Etan Reisner Dec 29 '14 at 18:31
  • @Schwern yes i found this script in this post, but as you can see it induced an issue. My question is about what can i do from now on (now that only my email adress is good) – An-droid Dec 30 '14 at 08:53
  • @EtanReisner yes it did and i don't understand why. I'm not very good with sbash ^^ – An-droid Dec 30 '14 at 08:54
  • All previous commits or all previous commit by you? The former doesn't make sense. The latter does (and should be fixable by specifying the revisions to rewrite more specifically on the `filter-branch` call). – Etan Reisner Dec 30 '14 at 13:22
  • @EtanReisner yes the good solution would have been to edit only my commits.. but as i said i'm not good with bash (and with git, i'm a beginner). Now i have to réedit the commits by mail adresse to restore the good ones. But i'm afraid the script will destroy the commits again. Any idea ? – An-droid Dec 30 '14 at 13:43
  • I don't know that you *can* restore the original commits. Even if you set the values back to their originals that may not work correctly. You may need to export your changes (with `git format-patch` or similar) and then import them (using `git am` I believe) into a new clone (this will actually let you modify the author information during the process as well actually. – Etan Reisner Dec 30 '14 at 13:46
  • if you have some times could you edit an answer, i don't think i fully understand what you suggest;/ – An-droid Dec 30 '14 at 14:10
  • @JulienM. I have updated my answer to cover how to deal with your devel branch. The fact that they're totally disconnected is odd, but shouldn't matter for the technique I'm using. – Schwern Dec 31 '14 at 22:18

1 Answers1

3

Since you only have a few commits to do, rather than the whole history, I would do it by hand using git rebase -i -p and git commit --amend --author "a.jard <a.jard@mail.com>".

It's covered in this answer which is not the accepted answer, but has double the votes.

As to why you're getting the result you are with your script, it's due to the nature of git and how rebase works. rebase does not rewrite history, it can't. Commits in git are immutable. The ID of a commit is tied to the content of the commit itself including the meta data like the date, log message, author and committer. rebase writes new history.

The other key to the puzzle is the ID of a commit is calculated using the ID of its parents. You cannot change the parent without also changing the children. This makes git push and pull very efficient, if I tell you I have commit ABC123 and if you have commit ABC123 we both know we have the same history.

For example, let's say you have a simple repository with five commits like this. master and origin/master both point at E.

A - B - C - D - E [master] [origin/master]

B has the wrong email address. A, C, D and E are all fine. You run your filter-branch command. It will look at A, see there is no change, and leave it alone. It will look at B, change the committer, and write a new commit with A as the parent. Let's call it B1.

A - B - C - D - E [master] [origin/master]
 \
  B1

Now it looks at C. There's nothing to change, but it needs its parent to B1. Because the ID includes the parent's ID, it must make a new commit.

A - B - C - D - E [master] [origin/master]
 \
  B1 - C1

And the same thing with D and E.

A - B - C - D - E [master] [origin/master]
 \
  B1 - C1 - D1 - E1

Done, filter-branch moves [master] to E1.

A - B - C - D - E [origin/master]
 \
  B1 - C1 - D1 - E1 [master]

And that is why changing one commit in the past will cause everything after it to diverge.

Because the author of that script didn't instruct you to limit which revisions git-filter-branch should filter, it did the whole history of the current branch.

Fortunately, you can undo this by moving master back to origin/master. There's several ways to do this. git branch -f master origin/master is simplest.

UPDATE This covers your new problem where your development branch is left hanging off the filtered branch. Let's start from the beginning. You had a situation like this...

A - B - C - D - E [master] [origin/master]

You ran git author-rewrite and wound up with this.

A - B - C - D - E [origin/master]
 \
  B1 - C1 - D1 - E1 [master]

You branched off master and started making new commits.

A - B - C - D - E [origin/master]
 \
  B1 - C1 - D1 - E1 [master] - F - G - H [devel]

You ran git branch -f master origin/master to undo your filter. Branches in git are just labels pointing at commits, so only the master label was moved. Your devel branch is still hanging off the filtered commits.

A - B - C - D - E [origin/master] [master]
 \
  B1 - C1 - D1 - E1 - F - G - H [devel]

Now you need to get devel and F, G and H hanging off master. First order of business is to move devel to master. If we do that it will be difficult to find F, G and H again. You could just write down the IDs, or you can take out some insurance with a tag. git tag tmp devel.

A - B - C - D - E [origin/master] [master]
 \
  B1 - C1 - D1 - E1 - F - G - H [devel] <tmp>

Now move devel to master with git branch -f devel master. The tmp tag keeps the filtered branch accessible.

A - B - C - D - E [origin/master] [master] [devel]
 \
  B1 - C1 - D1 - E1 - F - G - H <tmp>

Now you can use git cherry-pick to copy each individual change to devel. The content of the commit won't change, but the parents are, so they must be copied.

git checkout devel
git cherry-pick F^..tmp

To explain the F^..tmp part, we want everything from H to F. F..H says to include the parents of H, but exclude the parents of F, that's only H and G. Since we want to include F in the list, we use F^ to exclude the parents of F.

You wind up with this.

A - B - C - D - E [origin/master] [master] - F1 - G1 - H1 [devel]
 \
  B1 - C1 - D1 - E1 - F - G - H <tmp>

Once you've checked it's ok, delete the tmp tag with git tag -d tmp.

A - B - C - D - E [origin/master] [master] - F1 - G1 - H1 [devel]

Don't worry, if you messed up the commits will still be there for weeks before they are garbage collected.

Now you can check out devel and fix your commits using the rebase technique mentioned above. You'll wind up with this.

A - B - C - D - E [origin/master] [master]
 \
  B2 - C2 - D2 - E2 - F2 - G2 - H2 [devel]

Manually move master to E2 with git branch -f master E2.

A - B - C - D - E [origin/master]
 \
  B2 - C2 - D2 - E2 [master] - F2 - G2 - H2 [devel]

You will still have diverged. Pushing your changes will still need to be forced, and everyone else will have to force pull. That part cannot be avoided. Changing history after it's been pushed is always messy.

There are many other ways to accomplish all this. One of the advantages of using git filter-branch is it will move all tags and branches changed along the way for you. For small changes such as yours, and for new users, I prefer doing it in small steps. It's easier to understand what's going on.

Community
  • 1
  • 1
Schwern
  • 153,029
  • 25
  • 195
  • 336
  • if i use this last command. Won't there be à big part of trash history in my local repo ? (your explanation is really clear thanks) – An-droid Dec 31 '14 at 09:00
  • @JulienM. Yes. This is not a problem. Git's storage is extremely efficient. Git will garbage collect any unreferenced commits when it feels like it. This is a *good thing*. You can use `git reflog` to see the commits HEAD previously pointed to and use that to restore old commits after a mistake. – Schwern Dec 31 '14 at 21:49
  • thanks for your awesome answer, git is so powerfull but really complicated at first. Just to make sure some parts of the problem are the good ones : 1- the history under my origin/master doesn't have good mails, devel has 2- for now i have only pulled the project from the server, i've not pushed my feature yet 3- also i had at least 5 mails to change, so i ran the script 5 times ;/ – An-droid Jan 02 '15 at 09:11
  • @JulienM. The Git interface is complicated and hard. The Git internals are simple (in the sense of has few parts). 1 and 2 are correct. As to 3... which script did you run 5 times and with what arguments? – Schwern Jan 02 '15 at 18:42
  • i ran the script in my post several times qith diferents mail adresses (well your answer is really good so i'll accept it :) – An-droid Jan 05 '15 at 09:28