Subject: Re: Working copy revision and push pain

From: Junio C Hamano <[email-masked]>
Date: Sun, 23 Mar 2008 12:42:18 -0700

Junio C Hamano <[email-masked]> writes:

> Jonathan Watt <[email-masked]> writes:
>
> My understanding is if you push into a non-bare tree in merc, they make
> the branch head into "unmerged" state, and as far as the work tree is
> concerned you are still on the previous commit, and you merge "the other
> commit" that was pushed into from sideways to update the branch tip with
> pushed-into commit.
>
> In git, if you detach HEAD when you push into a checked-out 'master', your
> work tree won't be associated with 'master' anymore, and to merge the
> pushed-into state into the history of 'master', you would need:
> ...
> The second point bothers me quite a lot, as you are assuming that the
> user at the repository that was pushed into does _not_ know what is going
> on, and may keep working on a detached HEAD _without knowing_ what is
> going on.

Let's back up a bit and try again.

I was trying to say what Mercurial presents at the UI level may be much
nicer with respect to pushing into a checked out repository.  Their notion
of 'head' (and 'branch' in general) is quite different from ours in that
their branch can have more than one 'head's that the user can choose to
merge into its 'tip', and 'push' does not update the 'tip'.  It instead
adds a new (unmerged) 'head' or fast-forwards an existing 'head', and the
user can choose to merge them into 'tip' when convenient (please correct
me here, since I am talking from my reading their documentation and I may
have misread it).

In git, a branch is just a ref and points at a single commit, and push and
fetch updates a single ref.  There is no direct notion of "these other
refs are related to this branch ref and you may want to merge them" at the
low level plumbing layer, so if you push to update a ref, git does what it
is told to do.  The branch ref is directly updated, and won't get split to
be merged back later like Mercurial does.

My objection to the "always detach HEAD automatically" is because that
solution, while it may solve the issue of work tree and HEAD getting
inconsistent silently behind user's back, would introduce more confusion.

 - The repository loses where we were (so we would add a hack to record
   which branch you were on), and operations after that silent detaching
   will _not_ advance your local branch anymore, because the push took
   over the branch 'tip'.  If you switch to some other branch, you lose.

 - The final merge happens in the wrong direction.  The other commit is
   already called the official 'tip' of the branch because push took over
   the branch 'tip', and after merging it into your stale HEAD, you have
   to update 'master' with the merge made on the detached HEAD.

 - It's your repository and the commit you were on when this push from
   sideways happened should _stay_ as the official 'tip' of the branch,
   until you tell git so.  In that sense, pushing into a non-bare
   repository's refs/heads/ hierarchy directly is conceptually wrong,
   whether the branch in question is currently checked out or not.

Although we do not have their split branch heads, we do have a similar
concept by the name of "branch tracking".  Your 'master' is marked as
being related to 'remotes/origin/master' by "git clone" and by "git
checkout -b foo remotes/origin/foo", and recent "git checkout" to switch
to such a branch will report how diverged you are from the other party.

The mothership-satellite configuration described in FAQ is about using
this facility, because "git push" into a non-bare repository is exactly a
"git fetch" run in the reverse direction (i.e. fetching to pushed-into
repository from pushed-from repository).