Nobody can update any commit. That means they didn't, and you won't either.
You can, however, have the illusion of updating some commit. It's an illusion! It did not really happen! Always remember that, otherwise none of this makes any sense.
Git finds a commit by hash ID. The hash ID of some commit depends on the exact content of the commit. Once you, or whoever, has made the commit, that hash ID means that commit. It means that now. It means that tomorrow. It means that next year. It always means that commit, and no other commit, ever.
Humans, however, don't use hash IDs. (Quick: what are the hash IDs of the last ten commits you made? I don't know any of mine either. I don't even remember the special empty tree hash ID.) Humans use branch names: I made a commit on branchA
. That's what you said, more or less.
We have Git store hash IDs in branch names. Your branchA
stored the hash ID of your commit. Then you gave your commit, with its permanently-assigned forever ID, to some other Git in charge of some other repository. Because every Git computes hash IDs the same way, their hash ID for that commit was the same hash ID. They put that in their repository and it had the same hash ID and they set up their name branchA
to record the same big ugly hash ID.
Later, someone—not you—came along and squinted hard at that particular commit and said something like: Hah, I can do better! They took that commit off of their branchA
—the branchA
that is in that other Git repository that you have your Git talk to; your Git uses the name origin/branchA
for this—using some Git command, perhaps git reset --hard HEAD~1
. We don't know, or really care for that matter, exactly what Git command they used.
Having ripped that commit off that branch—it's still there in their Git repository, it's just not at the end of that branch now—they then added their "new and improved" version of that commit to their branchA
. We can draw that situation, that occurs in their Git repository, like this:
H <-- ???
/
...--F--G--I <-- branchA
The uppercase letters here stand in for the actual Git hash IDs (which we don't know, or really care about that much since we never memorize any of them). Your commit H
, which used to be the last commit on branchA
, is now floating free in space, with no name that finds it. Their new-and-improved version of your commit, which we're calling I
, is now the last commit on their branchA
.
This is not how Git is supposed to be used
Suppose for a moment that they had not torn your commit out by the roots, and instead, just added a commit. (This is how Git is supposed to be used, in general.) If they had done that, their Git repository would now have this sequence of commits in it:
...--F--G--H--I <-- branchA
That is, they'd have a new commit I
, but the parent of commit I
would be your commit H
. Their name branchA
would remember the hash ID of their commit I
, and their commit I
would remember the hash ID of your commit H
.
You would then be able to run:
git fetch origin
which, in your own Git repository, would result in this:
...--F--G--H <-- branchA
\
I <-- origin/branchA
Remember, your Git uses the name origin/branchA
to remember the hash ID they're remembering with their name branchA
. The things that actually matter to Git are the commits with their commit hash IDs, but Git gives us these names to help us find the hash IDs. Each repository has its own names: their name branchA
is for them to use, and your name branchA
is for you to use.
Had they added a commit on like this, you could then have your Git "slide your name forward" (and down) so that you would have, in your repository:
...--F--G--H
\
I <-- branchA, origin/branchA
Your Git and their Git would now both agree that the last commit in the branch named branchA
is commit I
(whatever its actual hash ID is). Your Git would hang on to your own name branchA
and your name origin/branchA
, but both would now point to existing commit I
.
If you need to fix up commit I
, you simply add a new commit J
to your repository:
...--F--G--H--I <-- origin/branchA
\
J <-- branchA
Note how your new commit points back to the (now-shared) existing commit I
, which adds on. You can now have your Git send new commit J
back, and ask them to set their branchA
name to point to commit J
.
Faking it: the illusion of changing or removing commits
This is the normal way to use Git: everyone adds commits to the collection, and nobody takes any way. Some people find this messy (because it is messy) and prefer to fake things as if the development happened much more cleanly. There's nothing really wrong with that. If commits H
and I
have no real value, and only commit J
should remain, we don't need to preserve H
and I
for all time: we can make a new and improved commit that's like J
, but that has G
as its parent:
H--I--J <- ???
/
...--F--G--J' <-- branchA
(in your Git repository, with origin/branchA
not shown). Suppose we now send commit J'
to the other Git, and force it to set its name branchA
to point to J'
. Then they, too, will have:
H--I--J <-- ???
/
...--F--G--J' <-- branchA
in their Git repository. Anyone who looks up commits by name only, and uses the name branchA
, will see commit J'
, and from J'
, will reach back to commit G
. It is as if commits H
and I
and J
never happened.
If you have the hash IDs of commits H
, I
, and J
memorized, or written down on paper or a whiteboard or something, you can still find these commits for as long as they stick around in each Git repository. How long that is depends on a number of factors. A standard "ordinary user" Git repository keeps these commits for at least 30 days by default. But if you don't know their hash IDs, how will you find these commits? The name branchA
won't let you find them, because the way Git works, it turns a branch name into the hash ID of the last commit on the branch, and then works backwards from there. The commit that comes before J'
is G
. It's not the hash ID of any of the now-invisible commits.
This is what you and your co-worker are doing. I do not know why you are doing it. If you both like it, you can keep doing it. It's more difficult and error-prone than just adding new commits, and it's easy to completely lose a commit, if you don't know all the secret ways of finding lost commits. I don't recommend working this way—but if you want to, now you know what you are doing.