On Wed, 29 Oct 2014 23:41:42 -0700 (PDT)
Anthony Berglas <aberg...@gmail.com> wrote:

> I am trying to do something really simple.  I want to commit local
> changes to a remote repository.  But along the way other developers
> modified the remote.  This appears to be very difficult to do in Git.
> 
> When I finished my changes I did a commit -a.  All good.
> 
> But then the push failed.

At that point, you should have stopped and reached for the goto Git guy
in your enterprise ;-)

The major design principle of Git is that it's a distributed VCS (DVCS).
With this in mind, you should understand that the repository you were
trying to push to is somehow special only by policy ("it's a central
repository for our project Foo everyone uses to collaborate") but it's
not in any way special to Git on either side of the wire.
This is the reason why pushes don't work like in Subversion: the latter
is a client-server system, where commits ever happen and are stored on
the server.

Note that this feature of Subversion you're lamenting actually allows
to create commits of the state which did not ever exist on any of the
clients.  No, really, suppose Alice changes foo.cpp then commits, while
Bob is changing bar.cpp seeing an old state of foo.cpp in his working
directory.  Then Bob commits his changes, and Subversion happily
records this commit even though Bob's changes will surely break with
what Alice committed.

Git, as other DVCSes, won't allow this--the history which is pushed
somewhere must have two crucial properties:
* It must represent the complete state of the project (you can't commit
  changes in just a bunch of files).
* It must descent from the line of history already there (that is, if
  you're trying to push commit D to a branch "master" in a remote
  repository, and that branch currently "ends" with commit B, your D
  has to descend from that B--one way of the other, for instance,
  it's OK to push ...B->C->D but not ...A->C->D).

That's why Git failed your push: it required you to reconcile your
local changes with the changes used to update the target branch in the
remote repository with some other changes you did not see yet.

There are two ways to do that: merging and rebasing.  Both are done
after (or along with) fetching.  What to use depends on the workflow
accepted by your team and the concrete situation at hand.

> git fetch ok.

That was a sensible thing to do.

> So I tried to checkout the origin/master.

...and that wasn't.

origin/master is a so-called "remote branch" or "remote-tracking
branch".  Remote braches are sort of bookmarks which show you which
were the state of branches in a particular remote repository last time
you fetched from it--in this case, the branch "master" in the repository
known locally as "origin".  These branches are not "yours" and thus you
never work on them--they're only for reference.

These are explained well in, say, Pro Git.  My stab at explaining them
on this list is [1].

> That gave me a "detached head", even though it looked like I was on
> head.

Being in a state with detached HEAD means that the HEAD reference which
always points to a state on which the index and the work tree are
currently based is currently pointing at a specific commit rather than
to the tip of some branch.  The difference is that when HEAD points to
the tip of a branch, recording a new commit moves that brach's tip to
that new commit while committing on a detached HEAD merely updates that
HEAD reference and nothing else.

Git moves the repository to this state when you're checking out
something which is not a (normal local) branch--say, a tag, a specific
commit or, as in your case, a remote branch.  The reason is simple:
when you check out a thing like those just enumerated, you don't have a
branch to update with the commits to be recorded.

For some reason, working on detached HEAD is considered an "ace"
practice while there is really nothing special if you clearly
understand the mechanics.  Seasoned Git users routinely go into that
state to try out "crazy ideas"--there's little sense in trying to come
up with a name for a temporary branch to work on when you can just
start working and only *if* it turns out to be useful do something with
these new commits--say, assign a branch to them or tag them.

> It said create a branch so I created abtmp (I do not actually want
> any branches).

Yes, the situation just started to get more confusing.

In fact, Git merely suggested you might want to create a branch, not
said you had to.  The whole reason is that, while being in a detached
HEAD state, you check out some other state, the work done in that state
is sort of lost (not really) but getting hold on it requires some
knowledge not available from most Git crash courses.

> Then merged origin/master back into abtmp (which seems
> the wrong way).

It depends.  You can trivially recover from merging into "a wrong
branch", as explained below, what follows is an explanation of which
side of the merge to pick, and why.  You might want to skip it for now.

You merged whatever state the target branch in the remote repo has to
the state of that branch in your local repo.
This might be just fine or not--depending on the project's (or branch's)
policy: some projects are fine with such merges while some others
require everyone that the "remote" state is always the side local
changes are merged *into.*

To describe this in other words...  Each merge commit has at least two
parents, the first parent is always the commit on which the index and
the work tree were based when the merge happened, and the second
(and possibly third etc) commits are those which were merged.
That is, the first parent is the "baseline" or "receiving" commit.

Let's now digress a bit.  One of the reasons merge commits happen is
because "topic" branches (implementing a particular feature of fixing a
bug) are integrated back to the baseline branch.  (To contrast, your
situation suggests your branch is not in that mode; instead, your team
seem to randomly commit on that branch.)
Some projects prefer maintaining some or all branches in a way that
merge commits recorded on them always have their first parents be the
baseline.  `git log` even has a special option to support that, named
"--first-parent"; you might want to read its description to get a
better idea on what I'm talking about.

When the devs merge like you explained (remote changes are merged into
local) the baseline is constantly flipped.  Again, that might be fine
and you might ignore the difference for now.

> So now I have the following.  What I want is to get rid of abtmp and
> commit back to origin/master on the remote server.
> 
> $ git log --oneline --decorate --graph --all
> *   5e0fcfb (HEAD, abtmp) Merge remote branch 'origin/master' into
> abtmp
> |\  
> | * 944773a (origin/master, origin/HEAD) - shrm has to be optional 
> logically (if s
> | * 4952f9c - correct to point by default
> * | 75b9d6d (master)  Performace tests
> |/  
> * c1106db - replace with st
> * b046367 - set back further
> * 5a3ce83 - fixup doc link reference
> * 2ca8ecf (tag: 7.0e) - this 
> 
> Questions:-
> 
>    1. How do I fix this up.

Note that in the `git log` output you cited all the "old" branches are
still there: your local "master" and "origin/master" containing the new
state on "master".

So:

  git checkout master
  git merge origin/master

(Here, possibly deal with merge conflicts; verify the new state works.)

  git push origin master

You then might want to delete "abtmp":

  git branch -D abtmp

>    2. What is the best way to deal with these simple conflicts in
> future.

Scenario one:

  git push origin master
  (...which failed)
  git fetch origin
  git merge origin/master
  (...possibly deal with conflicts; verify the new state *works*)
  git push origin master

Scenario two:

  git push origin master
  (...which failed)
  git fetch origin
  git rebase origin/master
  (...possibly deal with conflicts; verify the new state *works*)
  git push origin master

There are variations like `git pull` but IMO these contain too much
magic in them and it's better to understand the basics first.

[...]
> What is wanted is "How to use Git like Svn".

1. https://groups.google.com/d/msg/git-users/mJ0iOIZO8ak/M5WpwNix2lkJ

-- 
You received this message because you are subscribed to the Google Groups "Git 
for human beings" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to git-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to