Bug!?: Refspec '+' should be same as '--force' but is not

3 messages, 3 authors, 14d ago · open the first message on its own page

Bug!?: Refspec '+' should be same as '--force' but is not

From: André Kießling <hidden>
Date: 2026-09-15 15:22:05

Hi there, think I found a bug...

In https://git-scm.com/docs/git-push I find:
 > The + is optional and does the same thing as --force.

The fetch documentation refers to push for the details of <refspec> so I 
assume, the statement also holds for fetch refspecs.
However, this is not true:
When I run `git fetch origin --tags --force` it will force update tags 
that changed on remote (as expected) but will NOT delete local tags.
When I run `git fetch origin` with config set to `remote.origin.fetch = 
+refs/tags/*:refs/tags/*` it will delete my local tags as if prune was set.

I'm using Git 2.54.0.windows.1





Carneios GmbH

Am Borsigturm 64
13507 Berlin

Tel. +49 30 2332035-0
Fax +49 30 2332035-99

https://www.carneios.de
info@carneios.de

Geschäftsführer: Michael Giersch
Handelsregister: Charlottenburg HRB 138285
USt.-ID: DE 280 745 293

Re: Bug!?: Refspec '+' should be same as '--force' but is not

From: Ben Knoble <hidden>
Date: 2026-09-15 16:41:30

Le 15 sept. 2026 à 11:33, André Kießling [off-list ref] a écrit :

Hi there, think I found a bug...

In https://git-scm.com/docs/git-push I find:
quoted
The + is optional and does the same thing as --force.
The fetch documentation refers to push for the details of <refspec> so I assume, the statement also holds for fetch refspecs.
However, this is not true:
When I run `git fetch origin --tags --force` it will force update tags that changed on remote (as expected) but will NOT delete local tags.
When I run `git fetch origin` with config set to `remote.origin.fetch = +refs/tags/*:refs/tags/*` it will delete my local tags as if prune was set.

I'm using Git 2.54.0.windows.1
I think this behavior is described in git-fetch(1) in the PRUNING
section. It’s a bit opaque to me :) Still, you might take a look
there and see what you find. 

Re: Bug!?: Refspec '+' should be same as '--force' but is not

From: Junio C Hamano <hidden>
Date: 2026-09-15 19:35:01

André Kießling [off-list ref] writes:
Hi there, think I found a bug...

In https://git-scm.com/docs/git-push I find:
 > The + is optional and does the same thing as --force.

The fetch documentation refers to push for the details of <refspec> so I 
assume, the statement also holds for fetch refspecs.
A fresh clone typically creates

    [remote "origin"]
	url = ...
	fetch = +refs/heads/*:refs/remotes/origin/*

And indeed "+" there *is* optional.  If you remove "+" from there,
then your "git fetch" from origin will notice every time "origin"
rewinds its branches because it fails to update your remote-tracking
branches without forcing.  So it is like giving "--force" to allow
the origin rewind its branch tips hence your remote-tracking branches.

IOW, the statement holds for both push and fetch and the
documentation is correct.  But forcing vs not forcing should not
affect pruning.  They are totally separate concepts.  So quoting the
above documentation and then suddenly discussing if pruning takes
place or not is a bit jarring.
When I run `git fetch origin --tags --force` it will force update tags 
that changed on remote (as expected) but will NOT delete local tags.
When I run `git fetch origin` with config set to `remote.origin.fetch = 
+refs/tags/*:refs/tags/*` it will delete my local tags as if prune was set.

I'm using Git 2.54.0.windows.1
Even though I do not do Windows, this is so basic a thing that I do
not think there would be platform-dependent behaviour differences.

Unfortunately, the above does not reproduce for me.  Here is my
failed reproduction attempt.

First the set-up.

    $ rm -fr /var/tmp/x && mkdir /var/tmp/x && cd /var/tmp/x
    $ git init src
    $ cd src
    $ git commit --allow-empty -m initial
    [master (root-commit) 9edb8aa] initial
    $ git tag -m initial v0.0 master
    $ git for-each-ref
    9edb8aaed37552991a2c7578da7862ffecae1e30 commit refs/heads/master
    26fb2b9f313528b70566594e621d3873e9815415 tag    refs/tags/v0.0
    $ cd ..
    $ git clone --no-local src dst
    Cloning into 'dst'...
    remote: Enumerating objects: 3, done.
    remote: Counting objects: 100% (3/3), done.
    remote: Compressing objects: 100% (2/2), done.
    remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
    Receiving objects: 100% (3/3), done.
    $ cd dst
    $ git tag -m ours -f w0.0 master
    $ git commit --allow-empty -m second
    [master 4b48c71] second
    $ git tag -m 'our second' w0.1 master
    $ git for-each-ref
    4b48c71f0f2d3ca58eeed0d3afa71f23681dd98e commit refs/heads/master
    9edb8aaed37552991a2c7578da7862ffecae1e30 commit refs/remotes/origin/HEAD
    9edb8aaed37552991a2c7578da7862ffecae1e30 commit refs/remotes/origin/master
    26fb2b9f313528b70566594e621d3873e9815415 tag    refs/tags/v0.0
    cc47a66dac78d2838e767436a44b24aaca521ef7 tag    refs/tags/w0.0
    9f49b658425485deff4cc1c4ea52583bfecd294a tag    refs/tags/w0.1

The origin (src) has a commit and a tag that points at it, the clone
(dst) builds a commit on top of that, has two tags of its own.

Now the reproduction attempt comes.

    $ git config set remote.origin.fetch --append '+refs/tags/*:refs/tags/*'
    $ git fetch origin
    $ git for-each-ref
    4b48c71f0f2d3ca58eeed0d3afa71f23681dd98e commit refs/heads/master
    9edb8aaed37552991a2c7578da7862ffecae1e30 commit refs/remotes/origin/HEAD
    9edb8aaed37552991a2c7578da7862ffecae1e30 commit refs/remotes/origin/master
    26fb2b9f313528b70566594e621d3873e9815415 tag    refs/tags/v0.0
    cc47a66dac78d2838e767436a44b24aaca521ef7 tag    refs/tags/w0.0
    9f49b658425485deff4cc1c4ea52583bfecd294a tag    refs/tags/w0.1
    $ git config list --local
    core.repositoryformatversion=0
    core.filemode=true
    core.bare=false
    core.logallrefupdates=true
    remote.origin.url=/var/tmp/x/src
    remote.origin.fetch=+refs/heads/*:refs/remotes/origin/*
    remote.origin.fetch=+refs/tags/*:refs/tags/*
    branch.master.remote=origin
    branch.master.merge=refs/heads/master

Unless this is Windows specific, which I highly doubt, there must be
something that is missing from your report.  Perhaps you have some
configuration settings?
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help