August Feng

Learning and configuring git push

About

OK, let's take the time to learn the default behaviours of git push instead of fumbling around each time. ☺

I'm going to read the manual of the git for each things that I find a bit magical and will document them here.

Setup

After creating a repository on GitHub and following the initialization steps, we end up with .git/config file that looks like this:

[core]
	repositoryformatversion = 0
	filemode = true
	bare = false
	logallrefupdates = true
	ignorecase = true
	precomposeunicode = true
[remote "origin"]
	url = https://github.com/augustfengd/foobar.git
	fetch = +refs/heads/*:refs/remotes/origin/*
[branch "main"]
	remote = origin
	merge = refs/heads/main

The experience

Typically, one would branch off main, commit a few patches and then run git push:

# branch off
git switch -c feature

# commit some changes
echo foobar > helloworld.txt
git add helloworld.txt

# git push
git push

With an default setup, such an operation will be met with this familiar error:

git push
# fatal: The current branch feature has no upstream branch.
# To push the current branch and set the remote as upstream, use
# 
#     git push --set-upstream origin feature
# 
# To have this happen automatically for branches without a tracking
# upstream, see 'push.autoSetupRemote' in 'git help config'.

git push

Let's investigate what git understand when we run git push.

According to the man page of git-push, the interpretation of that command verbatim will be git push [<repository> [<refspec>…]]

<repository>

Assuming the .git/config from earlier, the behaviour of omitting <repository> is to default to origin.

This behaviour can be changed by configuring the branch in .git/config:

[branch "feature"]
	remote = xyz

[<refspec>…]

Assuming the .git/config from earlier, the behaviour of omitting [<refspec>…] is to use the simple mode from the list of predefined behaviour in push.default.

The simple mode says that git push should push the current branch to a branch of the same name on remote.

Configuring the branch

We define the upstream branch for a given branch by introducing branch.<name>.remote and branch.<name>.merge to the branch's configuration in the .git/config file:

[branch "feature"]
	remote = origin
	merge = refs/heads/main

An opinionated configuration

Note that the feature branch configuration from earlier is different than what would happen when we run the command from advice: git push --set-upstream origin feature.

The reason I target the master or main remote branch as upstream for my all branches instead of a remote branch with the same name is because:

  • These are the branches that I want my changes to eventually land on.
  • My branch's tracking status in relation to master is more informative than seeing how many commits I forgot to push.

In order to achieve this automagically, I need to change a few git settings.

The first one is to automatically setup the branch's upstream configuration upon branch creation.

branch.autoSetupMerge

This configuration controls this setup automation, and the inherit setting fits my need the most, since I want to keep the upstream for all my branches.

[branch]
	autoSetupMerge = inherit

push.default

This configuration controls what happen when we don't specify [<repository> [<refspec>…]] after the git push command.

It's default value is simple, which is to push to upstream.

In my setup, upstream is always master so I use the current setting instead which causes git push to push the current branch to one with the same name on the remote.

[push]
	default = current