Search code examples
gitgit-push

Default remote for Git push


In Git-config you can see:

branch.name.remote

When on branch <name>, it tells git fetch and git push which remote to fetch from/push to. The remote to push to may be overridden with remote.pushDefault (for all branches). The remote to push to, for the current branch, may be further overridden by branch.<name>.pushRemote. If no remote is configured, or if you are not on any branch, it defaults to origin for fetching and remote.pushDefault for pushing.

Now I have a cloned repository and a branch which is named test and is checkouted. Here you can see contents of config file of the cloned repository:

[core]
    repositoryformatversion = 0
    filemode = true
    bare = false
    logallrefupdates = true
[remote "origin"]
    url = ...
    fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
    remote = origin
    merge = refs/heads/master

As you see, branch.test.pushRemote, remote.pushDefault and branch.test.remote are not set there. So I expect when I do

$ git push

I will get

fatal: No configured push destination.

But I get

Everything up-to-date

It seems Git uses origin instead of not configured remote.pushDefault. But why when docs say

it defaults to remote.pushDefault for pushing.

Edit:

In Git-push you can see:

When the command line does not specify where to push with the <repository> argument, branch.*.remote configuration for the current branch is consulted to determine where to push. If the configuration is missing, it defaults to origin.

It think there is a conflict between using origin and remote.pushDefault.


Solution

  • Git's push code (and pull too, really) is (in my opinion) unnecessarily complicated because it tries to retain a large number of historical behaviors, several of which were bad ideas.

    Let's look first at the REMOTES section of the git-push documentation:

    The name of one of the following can be used instead of a URL as repository argument:

    • a remote in the Git configuration file: $GIT_DIR/config,

    • a file in the $GIT_DIR/remotes directory, or

    • a file in the $GIT_DIR/branches directory.

    All of these also allow you to omit the refspec from the command line because they each contain a refspec which git will use by default.

    (The name of a remote is—again, this is just my opinion—the only one of these that really should survive today. I think the specified-URL method probably should be moved to a plumbing command, and git push could return to being a simple script, although the Git-on-Windows folks are, alas, trying to convert away from scripts due to Windows performance issues.)

    Fortunately, you are using the named-remote method, so we can ignore most of this. Specifically, the refspec is optional because you are using a remote in $GIT_DIR/config.

    Next, we should clearly distinguish between syntax (nouns and verbs and such that you enter on a command line) and semantics (behavior). Once we toss out the extra varieties of "remote" we're left with two tasks:

    1. Select a remote.
    2. Select a refspec.

    The syntax you used was git push, i.e., no specified repository argument and no specified refspec. So for step 1, selecting a remote, Git uses the paragraph you quoted in your edit: find the current branch—in this case, test–and then look up branch.test.remote. This is not configured (is "missing"), so Git falls back to using origin.

    (There's a bug in the quoted documentation paragraph, as it fails to mention branch.branch.pushRemote, which in this case would be branch.test.pushRemote. The correct sequence is (1) look for the branch-specific pushRemote; (2) look for the branch-specific remote; (3) look for remote.pushDefault if Git version 1.8.3 or newer; (4) try the word origin. This documentation bug is still in git 2.8.1.)

    Since origin is a valid remote name, step 1 succeeds and we move on to step 2, selecting a refspec. This part does not have you puzzled, but just for completeness:

    When the command line does not specify what to push with refspec ... arguments or --all, --mirror, --tags options, the command finds the default refspec by consulting remote.*.push configuration, and if it is not found, honors push.default configuration to decide what to push (See git-config(1) for the meaning of push.default).

    So in this case, Git looks for remote.origin.push and, since that is not set, push.default. If push.default is not set, there is a "default default", which in Git prior to 2.0 was matching and is now (Git 2.0 or newer) simple. This default push.default means that there is always a refspec.

    The default remote-name origin means that there is usually a remote. This fails only when there is no [remote "origin"] section in your $GIT_DIR/config.