Note: I don't use these GUIs and sometimes they do something a bit unexpected. But in this case I expect it only does what I expect (as it were :-) ).
Fundamentally, git never actually changes anything in a commit. If you do an "amend" operation, even if you don't first back up one or two commits in a branch, what git does underneath is to make a new commit, leaving the old one in the repo.
For instance, your commit chain looks like this. All I have done is rewrite the vertical graphical version in a horizontal form (more suitable for text).
... - M - B - C - P <-- master, origin/master
/
...
where M
is the "Merge branch 'deploy" commit, B
is the first "blah, blah" commit, C
is the one you want to change, and P
is the pushed commit (the second "blah, blah" one).
Now, if you "detach" HEAD
so that it points directly to commit C
, you can run the command-line command git commit --amend
, which seems like it's changing C
. Git brings up an editor with the original commit message and you can change the text and write and exit the editor, and git makes a new commit. But what it does is not "change C". Instead, it makes a copy of C
, let's call it D
, with the same files but different commit-text. (If you modify some file(s) and git add
them before doing git commit --amend
, the changes you make to the files get associated with the new commit D
; again, commit C
remains entirely unchanged.) This gives you a commit chain that looks like this:
... - M - B - C - P <-- master, origin/master
/ \
... D <-- HEAD
It is now possible to take commit P
and copy it as well. (You still can't change it—this is a feature of a git commit. Git commits are frozen in time forever: their SHA-1 "true name" is a cryptographic checksum of your name as the commit author, the time-stamp at which you make the commit, the commit text, the commit's parent commits, and all the files and directories inside the commit; and changing a single bit anywhere in the commit, changes the SHA-1, and hence the "true name" of the commit. The result is a new, different commit.) Let's call the copy of P
(with parent changed to D
) E
:
... - M - B - C - P <-- master, origin/master
/ \
... D - E <-- HEAD
It's possible your graphical tool will do this for you automatically, if you say "please change commit C
": it will make a copy of C
, with whatever changes you like, which becomes D
; and then it will make copies of things on top of C
—in this case, that means a copy of P
—as well. (Or more likely it doesn't bother copying "things on top of C
" at all, forcing you to do it if you want that.)
You can now move your local branch label master
to point to commit E
instead of commit P
. In fact, git will sometimes even help you do that, with some commands. (I have no idea what your GUI will do, I find GUIs mysterious and often annoying. :-) ) But, if you do that, you will have a "divergent" master
branch, because the remote repo, the one at origin
, has your commits C
and P
(because you successfully "pushed" them earlier). And it has them on what it considers to be the master
branch.
Side note: if you can log in to the machine origin
, you can find the git repo over there and look at its idea of the various branch labels. Over there, you will see master
pointing to commit P
. That's why your repo has the "remote branch" label, origin/master
, pointing to P: essentially, whenever your machine contacts the remote, it asks: "hey! what labels do you have?" The remote answers—it might say "I've got master
and develop
and they point to commits P
and Q
"—and then your git copies these over to your repo, but changes the names: master
becomes origin/master
, develop
becomes origin/develop
, and so on. That's how you know what he knows. Or, more precisely, you know what he knew, the last time you talked with him. That could have been seconds ago and things might be totally different by now!
Anyway, suppose you move your master
to point to commit E
. If you now ask to push
these changes to origin
, your git will tell his git: "hey, I have these new commits D and E and you should point your master
to E
!" His git will look at those and say: "if I do that, I could forget about commits C
and P
. Your commit E
points back to D
and then D
points back to B
, and C
and P
will become unreachable." His git will reject your push unless you specify the "force" option.
If you do specify "force", his git might still reject it, but probably he'll take it. Is this a "bad thing"? Well, maybe. You'll be OK, and the repo at origin
will itself be OK. But what about Joe, who copied your commits C
and P
down from origin
just 17 seconds ago? Next time he goes to origin
he'll expect C
and P
to still be there, and they won't be. Joe will have to figure out that someone (you) "rewound" the branch, "removing" commits C
and P
.
Git is all set up for, and everyone expects to handle, new commits to get added. They don't usually expect old commits to "go away" like this. Joe can fix this—his repo will still have C
and P
; git doesn't actually delete commits (unless they become unreferenced and are garbage-collected). But he may have to do a lot of work to figure out what you did, and figure out that if he had new stuff that he had added on to P
, he now needs to copy that to new commits that add on to E
instead. (Specifically, he has to make sure he does not accidentally copy commits C
and P
, which are now subsumed by D
and E
.) So this could give Joe a headache. And if Joe has these, maybe Sally and Frank and Irene have those commits too, and maybe you will be giving dozens of people headaches.
Is it bad? Well, that depends on who has your old commits, how much of a headache you're going to give them by doing this, and whether you're allowed to give them headaches. :-) If nobody has them—if you're very quick, or the rate at which people pick up changes you push is measured in days instead of seconds—you'll be fine. If you give those who have your "bad" commits some warning, and they know what to do, you could be fine. If not, well, who knows?