Home > Blockchain >  'git pull' pulls the same data over again
'git pull' pulls the same data over again

Time:03-11

My local master pulls some branches (always the same) every time I call git pull. After those pulls I tried git pull origin master which fetched the origin master data and when I tried it again I got Already up to date, which was expected. But when I tried git pull again I keep getting the same branches creations. I thought that this might be caused by mismatch of masters in origin and local so I did git reset --hard origin/master and git pull origin master, the result Already up to date. Then I tried again git pull and I'm getting those branches again.

After calling git pull I see:

 * [new branch]          users/aaa -> origin/users/aaa
 * [new branch]          users/bbb -> origin/users/bbb
 * [new branch]          users/ccc -> origin/users/ccc
...
error: cannot lock ref 'refs/remotes/origin/users/ddd': is at 'hash1' but expected 'hash2'
...
 * [new branch]          users/eee -> origin/users/eee
...
! e7b282a57..aa76491e2  users/fff -> origin/users/fff  (unable to update local ref)
...

When calling git fetch --prune I got exactly the same result except I have also deletions at the top:

 - [deleted]             (none)     -> origin/Users/ggg/feat1
 - [deleted]             (none)     -> origin/Users/ggg/feat2
 - [deleted]             (none)     -> origin/Users/ggg/feat3
...(git pull result)

Scenarios I tried:

  • git pull origin master - Already up to date
  • git pull - pulls the same branches every time I call it
  • git reset --hard origin/master -> git pull - pulls the same branches every time I call it
  • git fetch - same result as git pull
  • get fetch --prune - pulls the same branches and removes the same branches everytime I call it
  • git merge - Already up to date
  • git branch –set-upstream-to=origin/master master -> git pull - same as normal git pull

I want to call git pull to have the same result as git pull origin master and not fetch the same data over and over again.

Can anyone explain what might be the issue here?

(If anyone wants me to add a test scenario to check the result I will add it)

CodePudding user response:

nneoneo has, in a comment, the right question:

Are you on Windows (or a case-insensitive filesystem)? Are you literally getting output that contains both origin/Users and origin/users (note case change)?

Git itself tries to be completely case sensitive. This means that one can create a branch named fool and a second branch named Fool and a third one named fooL and a fourth one named FOoL and so on. On a Linux system, for instance, this all works just fine. It works on Linux servers. It even sometimes works, sort of, on your standard Windows or macOS system that is set up to be case in-sensitive, where if you create a file or folder named users, any attempt to refer to a file or folder named Users after this actually refers to the existing users, and vice versa. That is, if two name components appear the same after case folding, the file or folder is created with whichever case you ask for at that time, and from then on, you cannot get the other spelling.

Git currently1 stores branch, tag, and other names2 in two different places, at various times:

  • A branch name may appear as a single line entry in .git/packed-refs (the line contains the branch name and the hash ID).
  • The name may be turned into a file name and stored in the .git directory as an OS-provided file: for instance, branch main's hash ID value might be stored in .git/refs/heads/main.

If both names exist, Git has the OS-provided file entry override, so that if we have a .git/packed-refs containing the line:

dcc0cd074f0c639a0df20461a301af6d45bd582e refs/heads/master

and a .git/refs/heads/main containing some other hash ID, the other hash ID is the hash ID that your branch main means.

Now, suppose that the source repository you're cloning and then later using with git fetch has at some point a branch named users/joe and a different branch named Users/joe. That source repository is stored on a Linux system, or perhaps by chance is always using the packed-refs file.

When you run git clone or git fetch, your own Git repository may create or update .git/refs/remotes/users/joe and/or .git/refs/remotes/Users/joe. If you are on a Linux system yourself, this will work just fine: you now have two different remote-tracking names for the two different branch names. But if you're using a case-insensitive macOS or Windows volume, and your Git software tries to do this, it will create just one of the two names, and then re-use the existing file for the other name.

This is exactly the scenario that gives rise to your problem. If your Git repository has only packed refs, things will work. If the upstream repository first fixes the case mix-ups so that all names are users/<name> and you use git pack-refs --all to pack all your refs, get rid of the contents of .git/refs to clean out any leftover trash, and then run git fetch --prune, the problem will go away. But the first step is to fix the upstream repository over at origin.

It seems that there was such policy in version control branch naming strategy and some people were creating users folder and some Users.

They need to stop doing that.

Strangely in Azure DevOps I cannot see folder with [capital-U] Users in list of branches ...

Azure DevOps has an add-on, not part of standard (C) Git, for dealing with related case issues. See, e.g., Azure Devops Git branch case sensitivity (which is about file name case issues). For branch and tag name issues see also the Azure online documentation.


1There is ongoing work to fix this, but it's not available in plain Git yet. Once it is, switching to the "reftables" back end will make this problem go away.

2All of these names in Git coexist by virtue of namespaces. A branch name is any name that starts with refs/heads/: for instance, branch main is really refs/heads/main. A tag name is any name that starts with refs/tags/: for instance, tag v2.1 is really refs/tags/v2.1. A remote-tracking name—a name where your own Git stores its renamed variant of some other Git repository's branch name—begins with refs/remotes/ and goes on to include the name of the remote, hence refs/remotes/origin/ for remote origin. After that comes the branch name as seen on the remote, i.e., the name with refs/heads/ stripped. This is how some other Git repository's main winds up stored in your Git repository as refs/remotes/origin/main.

  •  Tags:  
  • git
  • Related