问题
I have two remotes and two local branches:
- local branch "master" is tracking remote branch "origin/master"
- local branch "mirror" is tracking remote branch "github/master"
This is in my .git/config file:
...
[remote "origin"]
url = http://my.gitlab.com/nandoquintana/repo.git
fetch = +refs/heads/*:refs/remotes/origin/*
[remote "github"]
url = https://github.com/nandoquintana/repo.git
fetch = +refs/heads/*:refs/remotes/github/*
[branch "master"]
remote = origin
merge = refs/heads/master
[branch "mirror"]
remote = github
merge = refs/heads/master
[push]
default = tracking
This is the output of "git remote show origin":
$ git remote show origin
* remote origin
Fetch URL: http://my.gitlab.com/nandoquintana/repo.git
Push URL: http://my.gitlab.com/nandoquintana/repo.git
HEAD branch: master
Remote branch:
master tracked
Local branch configured for 'git pull':
master merges with remote master
Local ref configured for 'git push':
master pushes to master (up to date)
$ git remote show github
* remote github
Fetch URL: https://github.com/nandoquintana/repo.git
Push URL: https://github.com/nandoquintana/repo.git
HEAD branch: master
Remote branch:
master tracked
Local branch configured for 'git pull':
mirror merges with remote master
Local ref configured for 'git push':
master pushes to master (local out of date)
Both "push" and "pull" commands work properly:
- "push" command sends committed edits in a local branch to "her" remote branch.
- "pull" command brings commits from a remote branch to "her" local branch.
So, why "Local ref configured for 'git push'" is "master pushes to master"? why not "mirror pushes to master"? and what does "local out of date" means?
UPDATED after @torek answer:
Here we are some refs:
$ git ls-remote github
455063a9db09977535ac808af5729181b153f4c7 HEAD
455063a9db09977535ac808af5729181b153f4c7 refs/heads/master
$ cat .git/refs/heads/master
ca9e4399058a4998bd7c993f86d6740cfaec820b
$ cat .git/refs/heads/mirror
455063a9db09977535ac808af5729181b153f4c7
$ cat .git/refs/remotes/github/master
455063a9db09977535ac808af5729181b153f4c7
Exactly, "refs/remotes/github/master" and "refs/heads/master" are not equal. That is why "local out of date" message appears:
master pushes to master (local out of date)
That is not a problem for me, I positively know that code in "remotes/github/master" and in local "master" are different.
Nevertheless, code in "remotes/github/master" and local "mirror" are the same. In fact, refs "refs/remotes/github/master" and "refs/heads/mirror" are equal.
This is the message that would give me some peace of mind:
mirror pushes to master (up to date)
How could I configure remote/github... or push.default... to get this output?
回答1:
[Some reminders, to both myself and OP: remote origin
= GitLab, remote github
= GitHub. Fundamentally, the main problem with using git remote show
is that it makes assumptions. These assumptions are about how you will run other Git commands—git fetch
and git push
—in the future, and about how the second Git repository involved in each of those Git commands will be laid out in the future. Per, apparently, an old Danish proverb: It is difficult to make predictions, especially about the future.]
Let me address the edited question at the top here:
This is the message that would give me some peace of mind:
mirror pushes to master (up to date)
How could I configure remote/github... or push.default... to get this output?
There is one thing that is worth a try: one push.default
setting, upstream
, tells Git that by default, git push
should do this. if that works and does what you want, you're all set. But note that whatever Git says here, it's something of a lie anyway.
The fundamental problem is that mirror
doesn't push to master
. Until you enter a git push
command, it doesn't push to anything at all; once you do enter that command, you control where it goes with that git push
command, which can totally override any claim git remote show
made.
Your options at git push
time are:
Supply no additional arguments.
In this case, your Git picks the remote from the current upstream. If the upstream is set to
origin/...
the remote isorigin
. If the upstream is set togithub/...
the remote isgithub
. It looks like a simple string substitution (and usually it is, although for historical reasons, it's actually a horribly complicated string substitution that usually turns out to be simple: take the part before the slash).At this point, your Git proceeds on to the second case listed here.
Supply one additional argument. This argument names the other Git to connect to. It's usually a remote (
origin
orgithub
), though at this point you can give a URL instead, for instance.Supply two or more additional arguments. The first of these names the remote (or is a URL). The rest are refspecs, as defined below.
At this point, your Git connects to that remote and, in effect, runs git ls-remote
to see what branches they have. This list is important because it's used with the refspecs you gave, or failed to give, based on your push.default
setting, and the fact that when you do give a refspec, you can give just half a refspec.
It's the "half a refspec" case that's particularly problematic. No matter what else you do or set, if you actually run git push github mirror
while you're on mirror
, your Git will ask the Git on remote github
to set ... well, this depends on whether you have an upstream set for it, and if not, your push.default
setting. The details are in the git push documentation, in Git's usual cryptic form. The default default, though, is that for a "half refspec", the name mirror
means mirror:mirror
.
If you give a full refspec, e.g., git push github mirror:asdf
, the second half of the refspec determines which branch name your Git asks their Git to set. If you give half a refspec, the half you gave usually becomes both names. With the default of push.default = simple
, you can't accidentally push your mirror
to anyone else's master
, you must use the explicit full refspec (and then it's up to you to get it right).
If you give no refspec, your Git falls back on push.default
, and there are only five settings for it. The default one, simple
, has your Git compare your set of branches to their set of branches (from git ls-remote
). If your branch, such as mirror
, doesn't have any corresponding branch there, your Git won't ask their Git to set any branches.
Let's assume you are on your branch mirror
, which has its upstream set to github/master
, and that you have configured push.default
to upstream
(using git config push.default upstream
):
If you run
git push
with no arguments, orgit push github
with no additional arguments, the remote will begithub
.If you supply no refspec, the
push.default
setting applies: the refspec Git will construct will bemirror:master
.However, if you supply half a refspec, I'm not sure, even after re-reading the documentation several times, what full refspec Git will construct. I think it will be
mirror:mirror
, which is not what you want.You can also configure a
remote.github.push
variable that supplies default refspecs. This might also allow you to get what you want, althoughpush.default = upstream
seems simpler.
We now return to the original answer. :-)
So, why "Local ref configured for 'git push'" is "master pushes to master"?
This means that:
- assuming your current branch is
master
(i.e., you have rungit checkout master
) - and if you then run
git push github
(notgit push github more-command-words
) - Git—specifically your own Git—will attempt to push your current
master
tohttps://github.com/nandoquintana/repo.git
'smaster
which may or may not succeed, depending on what the Git server at GitHub does with a polite request to set its master
to whatever hash ID your Git sends with this push request.
(The same holds if you're on master
and run git push origin
, again with no additional arguments, except that the name origin
refers to the Git at GitLab.)
On the other hand, let's assume your current branch is named mirror
:
- so you just ran
git checkout mirror
- and now you run
git push github
- your Git will—based on what it sees right now, hence assuming this is what it sees again—not attempt to push anything.
The reason for this is most likely that you have push.default
configured, or defaulted, to simple
. When push.default
is set to simple
, a git push
without a refspec argument tries to push the current branch, but only if the other Git has a branch with the same name. So if the Git at github
(i.e., at https://github.com/...
) still does not have a branch named mirror
, your Git will say to itself: Huh. No branch named mirror
. Better not push anything.
(If, tomorrow, that other Git on GitHub does have a branch named mirror
, your Git will say to itself: Aha! There's the branch named mirror
! OK, I'll ask that other Git to update its mirror
.)
why not "mirror pushes to master"?
Because it doesn't. Even if you re-configure your push.default
, there's no default setting that means "if I fail to give git push
a refspec, make a non-matching one up."
A brief segment on refspecs
All of this is keyed on the concept of a refspec. In Git, a refspec is essentially a pair of references.
A reference is the fancy word for "branch or tag name". (It can be more than just those two, but those are the main two.) A branch name like master
is short-hand for its full spelling, refs/heads/master
. This reference name, refs/heads/master
, is Git's way to say "not just master
, but the branch master
". A short tag name like v1.2
is short-hand for the reference name refs/tags/v1.2
. If you leave out the refs/heads/
or refs/tags/
part, Git usually figures out which one you mean by looking at the branches and tags you have now.
Anyway, a refspec is mostly just two of these things with a colon :
in the middle:
refs/heads/master:refs/heads/master
for instance. You need two of them because you are using two Gits: one on your system, which you ask to do something, plus another one on some remote, such as gitlab
or github
. You have your Git call up the other Git over the Internet-phone. Then your Git and their Git talk to each other, after which your Git will fetch or push things.
The fetch and push steps need one reference for each Git, so that means you need two references: a refspec.
The two halves of a refspec are the source and the destination. If you're running git fetch
, the source is the other Git and the destination is your own Git. If you are running git push
, you are the source; the other Git becomes the destination. But in either case, the source gives some commits to the destination, and then the source's name—the left half of the refspec—is used to change something in the destination's name—the right half of the refspec.
For git fetch
, it's entirely normal to have a different name on each side. We fetch from their refs/heads/master
and write to our own refs/remotes/origin/master
. We fetch from their refs/heads/master
and write to our own refs/remotes/mirror/master
. This lets us fetch from a bunch of different places, yet keep them all straight.
For git push
, though, it's much more normal to use the same name on each side. We fetch from their master
, into our refs/remotes/.../master
. Then we work for a while and make sure whatever is in our master
is an update that goes on top of their master
, by merging or rebasing for instance. Then we call them up again, deliver our new commits, and ask them to set their master
—not their nando/master
, which they don't even have, but their master
—to this latest commit that builds upon their previous commits.
We make sure that it builds upon theirs, by fetching first, then working, then pushing. If we lose a "race" with Sofia—we both fetch about the same time, but she works faster and then she pushes before we can—we get that "remote rejected" "not fast forward" error; we must fetch again, to pick up Sofia's work from GitHub or wherever, and make our work build atop her work, and then try the push again.
and what does "local out of date" means?
When your git remote show
called up the remote—specifically github
, i.e., https://github.com/nandoquintana/repo.git
—over the Internet-phone, that other Git said:
I have these references:
refs/heads/master <some big ugly hash ID>
(try running git ls-remote github
to see what they have, you will get the complete list).
Your own Git has a branch named master
, but the big ugly hash ID your Git has for your refs/heads/master
—your master
branch—is different.
Since the two are different, your Git assumes you are either "ahead"—you have some commits they don't—or "behind" (they have some commits you don't), or perhaps both. Your Git can't tell how far behind you are, if you are behind at all, but it can tell how far "ahead" you are, by looking at all your commits. If you have commit 129bca4f...
whose parent is e033fc12...
, and they are at commit e033fc12...
, then you're ahead by just one commit.
If your Git can find their Git's commit hash ID in your master
branch's history, you are "ahead" and you can probably git push
right now to send them your new commits, asking them to set their master
to 129bca4f...
, and they will probably take the commits and update their master
.
But if they have commit 930ab988...
and you don't have that commit at all, all your Git knows is that they have some commits you don't. You must be "behind". You can git fetch
from them, get all the commits they have that you don't, and remember them with refs/remotes/github/master
. You can then do whatever it takes to add those commits to your own master
, so that you are even with them—neither ahead nor behind—and do any additional work it takes so that you are now ahead of them.
It's up to you to decide whether that's a good idea, and if so, whether to do it. All git remote show
does is call them up, using git ls-remote
, over the Internet-phone, and compare their references to your references to guess what git fetch
and git push
would do, based on those results. (If you use git pull
, that just means run git fetch
, then run git merge
. The git remote show
command tries to guess what that will do, too.)
来源:https://stackoverflow.com/questions/45960687/understanding-git-remote-show-command-output-which-is-the-meaning-of-loca