Intermittent build failures to execute push_to_git_remote & how to manage build number

Hey Bitrise team,

We are having build failings when calling the Fastlane action “push_to_git_remote” because of access . That’s because our CI lane is bumping the build number, commit and push it each time we deploy a build.

Error is:

[21:22:16]: Exit status of command ‘git push origin HEAD:HEAD’ was 1 instead of 0.
error: unable to push to unqualified destination: HEAD
The destination refspec neither matches an existing ref on the remote nor
begins with refs/, and we are unable to guess a prefix based on the source ref.
error: failed to push some refs to ‘’

The weird thing is once I get it to work, it usually after I add a new member to the app > team tab. Is Bitrise trying to commit and push with the last added user to the team rather than the “Service Credential User” that I chose in app > team tab?


1 Like

Thanks for asking this here! :slight_smile:

It’s not related to users at all, it’s related to how you start the build and to how git works.

If you start a manual build and you only specify a branch, then Git Clone will clone that branch.

But if you use webhooks to automatically trigger builds on code changes, will send the commit hash of the commit which triggered the build and Git Clone will “clone” that specific commit.

You can test this locally, if you do git checkout COMMITHASH you’ll get:

$ git checkout 6415740f2e73d65eb85969324d6d66f9a36bc70f

Note: checking out '6415740f2e73d65eb85969324d6d66f9a36bc70f'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b <new-branch-name>

HEAD is now at 6415740... commit message

Now, the thing about the detached HEAD state is that you can’t commit & push directly, without checking out a branch first. You can create commits, but if you’re not on a branch (detached HEAD is not a branch, in this state you’re not on any branch) you won’t be able to push the commit.

The git log actually includes the solution for the issue too: you can “get back to a branch” by git checkout -b BRANCH. Alternatively you could git checkout BRANCH before commit & push, to switch to a branch before you’d do your changes. Beware, if you do this you might commit on a different state of the code than what was build/tested during the build!

Imagine this: you push code to feature/a, which starts a build on with that specific commit, then you quickly push another commit to feature/a which starts another build. If the second commit lands before the first build would get to do a git checkout BRANCH, then git checkout feature/a might actually point to the second commit instead of the first one, as feature/a now has a new commit! You could possibly fix this by doing git checkout -b my_temp_bump_branch and then git merge the my_temp_bump_branch into the source branch (feature/a in the example).

You also have to be careful which branch you checkout, e.g. if the build was started by feature/a you should checkout that branch and not a hardcoded one (e.g. master)! You can get the build’s branch through the BITRISE_GIT_BRANCH env var (

In any case, this is how git works, so you can test this locally too. A webhook triggered build (when a commit hash is available) is similar to doing a

git checkout COMMITHASH

while if the build is started without a commit hash, only with a branch parameter, that’s similar to

git checkout BRANCH

You can test both on your own Mac and see what you have to do to make the tool you use to work with the git checkout COMMITHASH case.

One more important note: if you push back the generated version bump commit and you have a webhook which starts a build on for code changes, that push will also start a build, leading to a potential infinite build cycle! You can fix this by using the Skip CI feature, to skip this auto generated commit.

If you have any questions, just let us know!

What we usually do:

  1. We either bump the version number manually, treating it just like any other code change (creating a Pull Request too), as we believe version numbers should mean something and are part of the code - it’s a developer’s decision to declare a version as a new patch, minor or major change, you can’t really automate the decision whether the changes of this version are breaking changes or not. In this case we use the BITRISE_BUILD_NUMBER as the build number in the app, which does not require committing it into the code and this way you can link every build of the app to the build on Note: iOS apps have both a version number and a build number info, which is ideal to do this: manage the version number manually, and set the build number automatically, to the BITRISE_BUILD_NUMBER, e.g. by using the Set Xcode Project Build Number step.
    • This solution is the easiest to setup and manage, and it’s probably the best for App type projects and projects where you do periodic releases (weekly, monthly, …), but you don’t do multiple daily production deploys. We also use this type of versioning for e.g. our Build Steps.
  2. Or we don’t store the version in the code directly, we use git tags for versioning, which does not require a commit to be pushed, only git tag x.x.x && git push origin tags/x.x.x (this is mainly for web projects with continuous deployment, where a version number wouldn’t mean much in the code - but lately we try to switch to including the version number in the project and we use this less frequently, and use the previous solution instead in most cases)
  3. Or when we do auto generate a commit for the version bump we use Skip CI to not to trigger another build. But we only do this after careful configuration, and we usually don’t start with this setup. To do this we also use GitHub’s protected branches feature enabling pretty much every protection feature they have (e.g. that every Pull Request have to be up to date with “master” before it could be merged) and carefully configuring the flow (who can push to where, required code reviews, etc.). Once configured this can work really well for continuous delivery (e.g. we use this for the main server), but the configuration takes quite some time and experimenting, to allow your team to deploy frequently and without worrying, while ensuring code consistency.
    • This or the tag based solution are usually required when you do production deploys multiple times a day, as there you most likely don’t really care about the meaning of the version number, it’s more for identification and less for declaring (external) compatibility (~SemVer), users usually can’t even see this version number. We ensure external compatibility (e.g. the API) through tests, and that does not require SemVer type versioning. We use this type of versioning for our web servers (which we do CI/CD on of course ;)), where it’s quite common to have 10+ daily production releases.

Thanks Viktor for the thorough response!

We are coming from Jenkins where you can checkout to a specific local branch, so having Fastlane bumping the build number works for us. We also use gitflow workflow and we do that on our develop branch that autodeploys to Crashlytics Beta. Our master and release branches have more scrutiny :slight_smile: And yes, we use the [skip ci] to not be stuck in a loop :slight_smile:

Anyway, I think we may end up with option 1 and go the manual route. Upon merge from a feature branch to develop via a PR, we have a PR template with a checklist of stuff to be done. I’ll add an extra checkbox for bumping the build number or version code (Android) for . Let’s see how it goes with the team and we’ll refine process as we build more product increments.

1 Like

Awesome, let us know how it goes, or if you have any questions! :wink:

I’d also suggest you to use the BITRISE_BUILD_NUMBER as the build number, for automatic build number “bump” - it makes identifying builds way easier. You can also use the env var in Gradle configs directly, via System.getenv("BITRISE_BUILD_NUMBER")

def bitriseBuildNumber = System.getenv("BITRISE_BUILD_NUMBER")
def buildNumber = bitriseBuildNumber != null ? bitriseBuildNumber : "0"


def buildNumber = System.getenv("BITRISE_BUILD_NUMBER") ?: "0"


Don’t forget that you can automate this on Bitrise by using multiple Workflows and specifying which one to use with Triggers :wink:

Related docs, for something like what you described: