Re: [git-users] Re: keeping two bare repos in sync?

2013-10-03 Thread Konstantin Khomoutov
On Thu, 3 Oct 2013 06:45:14 -0700 (PDT)
dkoleary  wrote:

> Absolutely amazing, sir!  Your answer is spot on accurate, very
> complete, and very detailed. While I sorry that I put you to that
> much unpaid work, I am very grateful.  I'm going to bookmark this
> explanation and refer to it regularly until the concept sticks.  I
> have a grasp on it now; but, I'm not sure it's completely there yet.

Thanks for your positive feedback!

I've spotted a minor error in my explanation though: in the sentence

  Now when you call `git fetch origin` (or just `git fetch`) in a
  repository set up like this, Git sees no refspec passed to it on the
  command line and so it looks up the .fetch configuration
  variable in the local repo (in this case it will be origin.fetch), and
  if it's found, uses the refspec it defines.  Note that contrary to the

the names of the configuration variables should include the word
"remote": remote..fetch for the generic form and
remote.origin.fetch for a remote named "origin".  You can observe how
that maps to the configuration file sections:

[remote "origin"]
fetch = ...

means to read/write that "fetch" variable in a section we refer to it
by "remote.origin.fetch".

> Some interesting data points that will probably be explained by
> different git versions or the fact that I'm cloning from a bare repo:
> 
> * ``git clone --bare ${prod}:/${dir}`` results in a bare clone,
> obviously. The config file, though, doesn't have the remote origin
> stanza to which you referred, nor does ``git remote -v`` display
> anything.
> 
> # cat config
> [core]
> repositoryformatversion = 0
> filemode = true
> bare = true
> # git remote -v
> #

Quite probably, yes.  What does `git --version` return?

> *  Easily added and, once done, run the git config command you
> mentioned results in:
> 
> # git fetch -v
> From ${prod}:/opt/app/git/filemover
>  * [new branch]  master -> origin/master
>  = [up to date]  master -> master
> # git remote -v
> origin  ${prod}:/opt/app/git/filemover.git (fetch)
> origin  ${prod}:/opt/app/git/filemover.git (push)
[...]

Hmm, there might be a problem with your setup (not a serious one but
anyway): it's somewhat unlikely to see a branch named "origin/master"
in a bare (shared) repository because the name of this branch looks
like a typical name of a remote branch in a someone's normal repository.
So you might have inadvertently pushed a remote branch to your shared
repo using something like `git push --mirror`
(or `git push  refs/*:refs/*` which is essentially the same).
Don't do that from a non-bare repository having a typical setup.
There's no harm by why have this confusion?  If it turns out that I'm
correct and someone have inadvertently pushed a remote branch in the
shared repository you could delete it by pushing nothing to it:

git push  :refs/heads/origin/master

-- 
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/groups/opt_out.


Re: [git-users] Re: keeping two bare repos in sync?

2013-10-03 Thread dkoleary
Hey;

Absolutely amazing, sir!  Your answer is spot on accurate, very complete, 
and very detailed. While I sorry that I put you to that much unpaid work, I 
am very grateful.  I'm going to bookmark this explanation and refer to it 
regularly until the concept sticks.  I have a grasp on it now; but, I'm not 
sure it's completely there yet.

Some interesting data points that will probably be explained by different 
git versions or the fact that I'm cloning from a bare repo:

* ``git clone --bare ${prod}:/${dir}`` results in a bare clone, obviously. 
 The config file, though, doesn't have the remote origin stanza to which 
you referred, nor does ``git remote -v`` display anything.

# cat config
[core]
repositoryformatversion = 0
filemode = true
bare = true
# git remote -v
#

*  Easily added and, once done, run the git config command you mentioned 
results in:

# git fetch -v
>From ${prod}:/opt/app/git/filemover
 * [new branch]  master -> origin/master
 = [up to date]  master -> master
# git remote -v
origin  ${prod}:/opt/app/git/filemover.git (fetch)
origin  ${prod}:/opt/app/git/filemover.git (push)

* Updating a file, pushing to the prod repo and then fetching from the DR 
one work flawlessly.

Thank you again for the very clear, concise, and accurate answer.  

Doug O'Leary

-- 
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/groups/opt_out.


Re: [git-users] Re: keeping two bare repos in sync?

2013-10-03 Thread Konstantin Khomoutov
On Wed, 2 Oct 2013 12:04:28 -0700 (PDT)
dkoleary  wrote:

> Apologies for replying to my own post, but I did just find the "git
> --bare fetch".  While that seems to have worked, 

That's odd: `git fetch` does not support "--bare", only `git clone`
does.  If `git fetch` doesn't fail on this option, it's a bug, I think.

> # git --bare fetch ${prod}:/opt/app/git/filemover
> From ${prod}:/opt/app/git/filemover
>  * branchHEAD   -> FETCH_HEAD
> 
> how do I go about getting those changes into the bare repo?  A git
> log isn't showing the new commits yet...

Well, it's all in "the refspecs".  The refspec if short for "ref
specification" and "ref" is a short form of "reference" in Git's
parlance.  A ref is a symbolic name pointing to an object in the Git
database; branches, tags and notes are all example of refs.

Refspecs tell certain Git commands on which refs to operate, and how.
For instance, the full form of the `git fetch` call is

git fetch [OPTIONS] [ [ ...]]

where  is either an URL or the name of a named remote (those
managed with the `git remote` command).

Now let's cite the `git fetch` manual:

  

The format of a  parameter is an optional plus +, followed
by the source ref , followed by a colon :, followed by the
destination ref .

The remote ref that matches  is fetched, and if  is not
empty string, the local ref that matches it is fast-forwarded using
. If the optional plus + is used, the local ref is updated even
if it does not result in a fast-forward update.

and earlier there:

The ref names and their object names of fetched refs are stored
in .git/FETCH_HEAD. This information is left for a later merge
operation done by git merge.

The missing bit of information is that if Git fails to obtain a refspec
-- there's no refspec on the command line and none can be obtained from
the local repo configuration (more on this later) it assumes you want
to get whatever HEAD ref points in the remote repo, that is, if no
refspec is available Git uses HEAD, literally.

Now let's put the parts of "the puzzle" together: a call

git fetch 

is turned out to be

git fetch  HEAD

since no refspec is available.  Now Git contacts the repository at
, fetches whatever HEAD points there (in bare repos it's usually
refs/heads/master), writes all the objects fetched into the local Git
database and then writes the SHA-1 name of the fetched tip commit into
a special ref FETCH_HEAD.  No local branches are updated as the result
as there's nothing in the default refspec which tells Git to do so.

Now if you want to fetch all tags and branches from the remote repo and
update everything with matching names in a local repo, you'll have to
be clear about this:

git fetch  '+refs/*:refs/*'

which means "grab every ref from the remote side and update
corresponding refs locally with the fetched data".  I used single
quotes deliberately as you had to protect asterisks from being expanded
by the Unix shell.  A plus sign is there to force owerwriting the
local refs if the remote history changed in a non-linear way since the
last fetch.  If you only need branches (called "heads" in Git lingo) and
tags (but not notes, for instance), use several refspecs:

git fetch  '+refs/heads/*:refs/heads/*' '+refs/tags/*:refs/tags/*'

This information by itself is already a solution to your problem, but
let's dig deeper.

A part of "the magic" `git clone` does is to set up a single named
remote called "origin".  Setting up a named remote means creating a
special section in the configuration of a local repository -- in the
file named ".git/config" (or just "config" in a bare repository).
This section looks like this:

[remote "origin"]
url = REPO_URL_PASSED_TO_GIT_CLONE
fetch = +refs/heads/*:refs/remotes/origin/*

Notice the "fetch" variable there which defines a refspec for
`git fetch` (and, by definition, for `git pull` as well).
I copied this section from the configuration of a non-bare repo so you
might notice that the right-hand side of the refspec ("what to update")
speficies "refs/remotes/origin/*" and not just "refs/heads/*".  That's
because in non-bare repos `git fetch` by default updates the so-called
remote branches, and not your own local branches you're working on.

Now when you call `git fetch origin` (or just `git fetch`) in a
repository set up like this, Git sees no refspec passed to it on the
command line and so it looks up the .fetch configuration
variable in the local repo (in this case it will be origin.fetch), and
if it's found, uses the refspec it defines.  Note that contrary to the
default refspec, HEAD, this one set up by `git clone` actually
specifies what to update with the fetched data.

Now let's turn to `git clone --bare`.  As specified in its manual page,

  --bare

Make a bare GIT repository. That is, instead of creating
 and placing the administrative files in
/.git, make the  itself the $GIT_DIR. This
obviously implies the -n beca