-1

When I run git push -u origin main on the terminal, it says error: src refspec main does not match any.

But when I run git push -u origin master, it works correctly and it creates another master branch in the repo along with the default main branch and the total branches will be 2.

At this point, I can't do a PR to merge the master to the main and the error message is there's no diff between master and main branches.

Can anyone explain this condition and give me a working solution? Your support will be highly appreciated.

aynber
  • 22,380
  • 8
  • 50
  • 63
Kalhara Tennakoon
  • 1,302
  • 14
  • 20

4 Answers4

2

I'm pretty sure you'd like a simple formula, do X then Y and it will all work. Unfortunately we can't give you this without more information from you. At the end, I'll suggest a few possibilities, but you must inspect both your commits, and the commits found in the repository over on GitHub.

What's going wrong

The ultimate problem here is that Git is not about branches. Git is all about commits. Branch names do matter, because branch names help us—and Git—find the commits. But it's really the commits that matter. The branch names only matter for finding the commits; after that, the names stop being important—which is why you're free to change them at any time.

The commits themselves are numbered. Those commit numbers, or hash IDs, are the way Git really finds the commits. If you have the number, that's all you really need: give that to Git and Git will find the commit, if you have it. The problem with the numbers is that they're incomprehensible and impossible for humans to deal with. For instance, 2283e0e9af55689215afa39c03beb2315ce18e83 is the hash ID for a commit in the Git repository for Git. Nobody wants to remember this! So we use a name like master or main to remember it for us.

When you use git push, you are telling your Git to call up some other Git, in this case over at GitHub (origin = https://github.com/randiltennakoon/my-bmi.git, per your comment). Your Git and their Git have a conversation. Your Git lists out your commit hash IDs, and theirs lists out theirs (with a lot of fancy algorithmic work to minimize this communication) so that your two Gits can agree which commits you have, that they don't, that your Git wants to send to them.

Then, your Git sends these commits to them. They put them in a temporary quarantine area while they check out the rest of what you want to do. Your Git now asks their Git to set some of their branch names, to remember these new commits.

Your Git can ask their Git to set their master. If you tell your Git to do that, and they don't have a master, they'll generally say "OK!" and do it. After that, they will have your commits, under their name master. But to do that you'd have to run a Git command you haven't run (yet).

Your Git can ask their Git to set their main. If you tell your Git to do that, and they do have a main, they'll take a look at the commits you just sent. Do those commits add on to their main? Or do those new commits just wipe out their existing commits, as found from their name main?

This is where your later push, in that comment, is failing:

$ git push -u origin main
To https://github.com/randiltennakoon/my-bmi.git
 ! [rejected]        main -> main (non-fast-forward)
error: failed to push some refs to 'https://github.com/randiltennakoon/my-bmi.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

Here, you changed your name master to your name main, to match their name main. That's fine on its own—you can use any names you like, and most people find it helpful to use the same names in their Git as someone else (e.g., GitHub) is using. Then you asked your Git to call up their Git, send them your main-branch commits, and ask them to set their main to remember your last commit.

Here, we need to take a side trip. I'll try to keep it brief but there is a lot to know here!

What's in a commit

We've already mentioned that Git is all about commits, and that they are numbered. If you run git log, your Git will show you your commits—all of them, in your case, because you have only one branch—along with their hash IDs (commit numbers), in reverse order.

These numbers will look random. They're actually not random at all: they are computed with a cryptographic hash from the entire contents of the commit. This is how Git makes sure that every commit gets a unique number. But it also means that no part of any existing commit can ever be changed. Whatever numbers your existing commits have, those are their numbers. Every Git everywhere—yours, the one over on GitHub, mine, Fred's, every Git—agrees that those are the right numbers for those commits. No other commits get those numbers, ever, anywhere. This is why the numbers are so big and ugly.

Now, every commit—regardless of its number—consists of two parts:

  • One part of a commit is a full snapshot of every file that Git knew about, as of the time you, or whoever, made the commit. That is, after your git add . (or whatever) and git commit, Git made a snapshot of all of the files it knew about at that time: that's any you added, plus any it already knew about. Git actually uses the versions of those files that are in Git's index a.k.a. staging area at this time, rather than the versions you can see and work with, but we'll skip over this detail.

  • The other part of a commit is what Git calls its metadata, or information about the commit itself. Here, Git stores your name, as the author of your commit, and your email address. Git adds a date-and-time-stamp. Git includes your log message where you explain why you made this commit (not what's in it, which we can see from the snapshot part of the commit, but rather why you did whatever you did, which we can't see from the snapshot). And, crucially for Git itself, Git adds the hash ID of the previous commit.

These hash IDs wind up forming a backwards-looking chain. Let's draw one, using single uppercase letters to stand in for the mind-bending hash IDs:

A <-B <-C

This is a simple chain of three commits, as you'd find in a nearly-new Git repository. Commit A is the very first commit. It doesn't have any earlier commit—it can't; it's the first commit—so it doesn't point backwards at all. Commit B, however, is the second commit, so it points backwards to A, the first commit. Commit C, currently the last commit, points backwards to B.

In reality, commit C literally contains the actual commit number—hash ID—of earlier commit B, as part of C's metadata. Similarly, B contains the hash ID of earlier commit A.

In order to find all three commits, Git just needs the hash ID of the last of these three commits. We have Git stick this hash ID into a name, like master:

A--B--C   <-- master

If we change the name from master to main, none of the commits change:

A--B--C   <-- main

So this is trivial: we can change our branch names around all we like. A git checkout of whichever branch name we pick will actually get commit C—whatever its real hash ID is—because the one name we have, has the hash ID of commit C in it.

If we make a new commit, our new commit—which we'll call D, although in reality it gets some big ugly hash ID—will contain, as its backwards link, the hash ID of commit C:

A--B--C--D   <-- main

This is how a branch grows. The backwards connection from D to C says that the parent of commit D is commit C. The parent of C is B, and so on, except that once we hit A, there's no parent at all, and it all stops.

GitHub has a Git repository too

Meanwhile, over on GitHub, as found by the URL https://github.com/randiltennakoon/my-bmi.git, there's another Git repository. This other Git repository has some commits. Those commits have some hash IDs.

If those commits were the same commits that you have—if they had the same A-B-C for instance—you'd be in good shape. You'd have your Git call up their Git. Your Git would list out hash ID D. They'd say: Hey, I don't have D, why don't you give me that one? Your Git would add D to the list of commits to send over. Then your Git would say the parent of D is C. They would check and see that they do have C. This means they also have A and B and so the only commit your Git has to send is D.

Then, your Git would send just D, and ask them to set their main to remember D instead of C. That would be fine, because D itself remembers C. You're just adding on to their commits.

Alas, this is not what's happening. Instead, they have, e.g.:

G--H   <-- main

in their repository. So if you send them your entire series of commits, they'll end up with:

A--B--C--D

G--H   <-- main

Then your Git ask them to set their main to point to D. This will lose their commits. The name main can only point to one last commit, such as H or D. Switching to D does not add on to their commits. So they refuse.

How you fix it

What you need to do is:

  1. Obtain their commits.
  2. Decide how to combine your commits and their commits.

Step 2 is a big decision. It's the one we can't make for you.

Step 1 is easy: just run git fetch. When it's done, you will have an origin/main. This name is not a branch name, but it's almost as good in most ways, and better in one specific way: It's how your Git remembers their Git's branch name. Each git fetch you run will get any new commits from them, then update your memory of their Git's main branch name (and any other branches).1

In any case, this origin/main lets you see their commits—now in your repository too, found by the same hash IDs that their Git is using, because every Git agrees that those commits get those IDs. You now have their commits and your commits. These don't hook up with each other, because you made your commits completely independent of theirs, by making yours without starting with theirs.

Run:

git log origin/main

to see all of their commits. What commits do they have? What's in those commits? Are they important? Should you keep those commits? If so, how do you want to combine your commits with theirs?


1The exact details here depend on what kind of git fetch you run. The git pull command, for instance, runs a limited git fetch that doesn't pick up everything.


Combining method #1: git merge

The simplest method of combining two strings of commits is to use git merge. This is still true in your case, but there's a hitch.

Normally, with git merge, we start with something like this:

          I--J   <-- ours
         /
...--G--H
         \
          K--L   <-- theirs

That is, we have a branch—we're calling it ours in this drawing—that ends at commit J, and they have a branch that ends at commit L. We have Git combine these two branches. Git does so by finding commit H, which Git calls the merge base. You can see from this drawing that commit H is the best shared commit. We start at the two branch ends—commits J and L—and begin working backwards, the way Git always does. From J, we go back to I, then to H, and G, and so on. From L, we go back to K, and then H and G and so on.

These two traversals meet up at commit H. They keep meeting up from there on back, so commit H is automatically the best (i.e., last) commit at which the two branches met. The merge operation then figures out what we've changed since commit H, and what they changed, and combines these changes.

I don't know precisely what's in your Git and their Git, but I do know, from the way you went about making these two Git repositories, that what you have and what they have never meet up. You have something like:

A--B--C--D   <-- main

G--H   <-- origin/main

If we start at each end and work backwards, we never meet up.

Since Git version 2.9, git merge won't merge these by default. It just complains that the histories are unrelated, and stops. You can tell Git to go ahead and merge anyway, using --allow-unrelated-histories.

Whether this works at all depends on what's in the snapshots in D and H (or whatever last commits you have). If you get merge conflicts, you must resolve the merge conflicts yourself. Once that's all done—all conflicts are resolved, or there were no conflicts—Git can make a new merge commit, which I'll call M for Merge:

A--B--C--D
          \
           M   <-- main
          /
G--------H   <-- origin/main

What's special about commit M is that instead of just one parent, such as D or H, it has both of these as its two parents. So it combines the two separate chains of commits.

Assuming you want to make M, and did make M, you can now run:

git push -u origin main

You'll send, to the other Git, commits A-B-C-D-M. Commit M links back to commit H, which is where their main is right now. So if they obey your Git's polite request to set their main to point to M, they'll keep their G-H commits too. They'll decree that this is fine.

What's in commit M as a snapshot is the result of combining the two commits. If you have to do the combining by hand, because of merge conflicts, you pick what goes into commit M, as a snapshot. If Git is able to do the combining on its own, Git will make commit M on its own, and it will have everything you had in D, and everything they had in H.

Combining method #2: git rebase

The rebase command is quite complicated and I won't explain it properly here. See other StackOverflow answers for more. Instead, I'll just note that it works by copying commits, as if by running git cherry-pick.

Let's assume that you do indeed have this:

A--B--C--D   <-- main

G--H   <-- origin/main

If you run git rebase origin/main, your Git will save the four hash IDs for commits A-B-C-D, in a temporary file. Your Git will then use git cherry-pick or equivalent to copy each commit, in the A-B-C-D order (forwards! Git has to list them out backwards, then reverse the backwards list to do this), to come after commit H. If we call the copy of A, A', and the copy of B B', and so on, we can draw the result like this:

A--B--C--D   <-- main

G--H   <-- origin/main
    \
     A'-B'-C'-D'   <-- ???

The question marks show that there's a problem here: how will your Git find the copy D'? The answer is: it yanks the name main off the old series of commits, and pastes it onto the end of the new series. The result is:

A--B--C--D   ???

G--H   <-- origin/main
    \
     A'-B'-C'-D'   <-- main

It's now very difficult to find the old commits. If you saved their hash IDs somewhere (on paper, on a whiteboard, in a file, whatever), you can use those. There are other tricks Git has to help you find these commits for a while—for at least another 30 days, by default—but for most purposes commits A-B-C-D seem to be gone. As a result, when you use git log to look at your commits, you'll now have only six commits, not ten: git log will show you commit D', then C', and so on. When it gets to A', well, commit A' does have a parent: it links back to commit H. So after A', git log shows H, and then G, and then stops because the commits have run out.

You can now run:

git push -u origin main

Your Git will send commits A', B', C', and D', and ask their Git, politely, if they'll set their main to point to D'. Since D' extends their branch, they'll say OK.

Combining method #3: run roughshod over them

Suppose that, upon looking at their commits, you decide that those commits are utterly worthless. There's no harm in just throwing them away entirely.

In this case, you can use what Git calls a force push, git push -f or git push --force-with-lease. The --force-with-lease version adds a bit of extra safety (and is generally the way to go) but I won't go into any detail here about what this safety is.

What these options do is tell the other Git, in this case, over at GitHub: I command you, replace your main with my commit hash ID! That is, unlike a regular push, it's not a polite request. It's an outright command. They don't have to obey, but if you own that repository, they will.

Obeying your push means that they take your A-B-C-D chain (or whatever it is) and make their main point to the last commit in this chain, just like your existing main. So if you have:

A--B--C--D   <-- main

G--H   <-- origin/main

and you force-push to GitHub (git push --force-with-lease -u origin main), they'll set their main to point to D. You end up with:

A--B--C--D   <-- main, origin/main

G--H   ???

Commits G and H appear to vanish. They'll actually vanish for real from the GitHub repository some time in the future—it's hard to say when as that's up to GitHub, but the 30 day rule that works in your Git does not apply. They might go away immediately, or in a day or so.

Because you did get them into your own (local) Git repository, your Git will hang on to G-H for the at-least-30-days, should you decide that they were valuable after all. Finding them can be tricky though! We (humans) use names, not hash IDs, for a reason.

torek
  • 448,244
  • 59
  • 642
  • 775
  • I really appreciate your solution. But still, I have a question regarding this. Previosly GitHub doesn't contain `main` branch in default. But recently, I got to know they have changed their `master` to `main`. – Kalhara Tennakoon Feb 22 '21 at 03:16
  • So, when I create a new repo in GitHub, it automatically create the `main` branch as the default one. Then when we need to push our existing project to that particular GitHub repo, what we basically do is, we initiate git using the `git init` command. Then adding files using `git add ` and after that `git commit -m "msg"`. – Kalhara Tennakoon Feb 22 '21 at 03:16
  • Then `git remote add origin ` then we use the `git push` command. In my case, I firstly used, `git push -u origin master` command and it didn't work. Actually, I wanted go back to the beginning where this problem occurred and I hope you will have an idea now where I exactly got the problem. – Kalhara Tennakoon Feb 22 '21 at 03:17
  • The thing is, a Git branch *cannot exist* without commits on it. You (or someone) must make *at least one commit* first. After that, an infinite number of branch names can be created: all will identify that single first commit. Once you (or someone, anyone) makes a *second* commit in that Git repository, the potentially-infinite number of branch names can each select one of the *two* commits that exist; and so on. – torek Feb 22 '21 at 04:40
  • This means that for GitHub to create a `main` or `master`, they (GitHub) must first create *at least one commit*. Once this *commit* exists (and has a commit number / hash-ID), *then* they can create a branch named `main` or `master`. You must obtain *this commit* and decide whether you want to keep it and build on it, or throw it away. See the rest of my answer for the rest. – torek Feb 22 '21 at 04:42
  • I just wanted to do is `git branch -m master main` and `git push -u -f origin main` – Kalhara Tennakoon Feb 22 '21 at 04:43
1

It sounds like your local branch is called master, whereas the remote branch is called main. You have two options:

  • Rename your local branch:

    git checkout master
    git branch -m main
    git push -u origin main
    
  • Push your local branch to a differently named remote branch:

    git push -u origin master:main
    
knittl
  • 246,190
  • 53
  • 318
  • 364
  • I tried the first option. But still gives the same error. `git push -u origin main To https://github.com/randiltennakoon/my-bmi.git ! [rejected] main -> main (non-fast-forward) error: failed to push some refs to 'https://github.com/randiltennakoon/my-bmi.git' hint: Updates were rejected because the tip of your current branch is behind hint: its remote counterpart. Integrate the remote changes (e.g. hint: 'git pull ...') before pushing again. hint: See the 'Note about fast-forwards' in 'git push --help' for details.` – Kalhara Tennakoon Feb 21 '21 at 15:43
  • @RandilTennakoon: your local branch and the remote branch have different commits. "See the 'Note about fast-forwards' in 'git push --help' for details" (either somebody else has already pushed or you are working on a completely different branch) – knittl Feb 21 '21 at 15:46
  • git branch > main git branch -r > origin/main – Kalhara Tennakoon Feb 21 '21 at 15:47
  • I don't have an idea to fix this, any step-by-step guide? – Kalhara Tennakoon Feb 21 '21 at 16:06
  • @RandilTennakoon have you read the "Note about fast-forwards"? It is difficult to give general advice, because I don't know how your local and remote history looks like. But Git's help should provide a good overview – knittl Feb 21 '21 at 16:09
0

It wasn't so hard to perform this action actually. What I found was when we initialize a new GitHub project, it automatically creates the main breach as the default branch.

Then we can push our existing project to GitHub using the below commands.

git init
git add <file-name>
git commit -m "msg"
git remote add origin <remote-URL>

Next, before moving to the git push command, let's run git branch command to check available branches. git branch shows the local branches while git branch -r shows the remote branches.

Here's my output.

randiltennakoon@Randils-MacBook-Pro test_bmi_app % git branch
* master
randiltennakoon@Randils-MacBook-Pro test_bmi_app % git branch -r

You can see there's no output for git branch -r command yet. But it shows the master as the local branch.

Then we need to run the below command to move master to main

git branch -m master main

Then you can run the git push command along with the -f flag as below.

git push -u -f origin main

Then it will push your changes to the remote main branch and you can verify it by going to your repo in GitHub.

Then you can issue the below commands to verify and this works pretty well.

git branch
git branch -r

OR

git branch -a
Kalhara Tennakoon
  • 1,302
  • 14
  • 20
  • Using `git push -u -f origin main` is what I suggested as solution #3, **after** determining that you want to **throw away** the commit(s) in the GitHub repository. If you do this without looking, you're throwing away stuff you haven't looked at: is that a good idea? – torek Feb 22 '21 at 04:43
  • yeah, that worked. Thanks for the support. Your info helped me to get the direction – Kalhara Tennakoon Feb 22 '21 at 04:45
  • 1
    Note that when you create a GitHub repo, you can choose to create it with *no commits* (and no initial branch), which removes the need for the `--force` option. That might be the route you want for future repositories. Alternatively, after creating a GitHub repo, instead of using `git init` and `git remote add` locally, use `git clone` to *copy* the GitHub repo to a new repo on your local system, which will get the initial commit they made so as to have an initial branch. (GitHub is trying to do a service by creating this branch for you—whether it's a service or disservice is up to you!) – torek Feb 22 '21 at 04:46
-2

Maybe you just need to commit. Please run the below commands;

git add . (Don't forget the dot at the end)

git commit -m "initial commit"

git push origin main

Sohail
  • 303
  • 1
  • 10
  • Please let me know, if this doesn't work. – Sohail Feb 21 '21 at 14:22
  • Please go through the below link as well. https://stackoverflow.com/questions/4181861/message-src-refspec-master-does-not-match-any-when-pushing-commits-in-git – Sohail Feb 21 '21 at 14:29