{"thread":{"id":"47821","subject":"Re: linux-next: unnecessary merge in the v4l-dvb tree","startedAt":"2018-02-12T21:15:22Z","lastAt":"2018-02-16T21:27:24Z","messageCount":15,"participants":["Linus Torvalds","Mauro Carvalho Chehab","Junio C Hamano","Eric Sunshine"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"339111","messageId":"CA+55aFwM0vy+pw-Xv=gA19ULMwAXNPhdO3qR5A3hkMrZKJFNSQ@mail.gmail.com","threadId":"47821","inReplyTo":"20180213080036.3bf3a908@canb.auug.org.au","subject":"Re: linux-next: unnecessary merge in the v4l-dvb tree","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2018-02-12T21:15:04Z","receivedAt":"2018-02-12T21:15:22Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"On Mon, Feb 12, 2018 at 1:00 PM, Stephen Rothwell <sfr@canb.auug.org.au> wrote:\n>\n> Linus, this happens a bit after the merge window, so I am wondering\n> about the rational of not doing a fast forward merge when merging a\n> signed tag (I forget the reasoning).\n\nThe reasoning is to avoid losing the signature from the tag (when\nmerging a signed tag, the signature gets inserted into the merge\ncommit itself - use \"git log --show-signature\" to see them).\n\nSo when I merge a signed tag, I do *not* want to fast-forward to the\ntop commit, because then I'd lose the signature from the tag. Thus the\n\"merging signed tags are non-fast-forward by default\" reasoning.\n\nBut, yes, that reasoning is really only valid for proper merges of new\nfeatures, not for back-merges.\n\nThe problem, of course, is that since git is distributed, git doesn't\nknow who is \"upstream\" and who is \"downstream\", so there's no\n_technical_ difference between merging a development tree, and a\ndevelopment tree doing a back-merge of the upstream tree.\n\nMaybe it was a mistake to make signed tag merges non-fast-forward,\nsince they cause these kinds of issues with people who use \"pull\" to\nupdate their otherwise unmodified trees.\n\nI can always teach myself to just use --no-ff, since I end up doing\nthings like verifying at the signatures anyway.\n\nJunio, comments?\n\n               Linus\n"},{"id":"339113","messageId":"20180212193617.4993398a@vento.lan","threadId":"47821","inReplyTo":"CA+55aFwM0vy+pw-Xv=gA19ULMwAXNPhdO3qR5A3hkMrZKJFNSQ@mail.gmail.com","subject":"Re: linux-next: unnecessary merge in the v4l-dvb tree","fromName":"Mauro Carvalho Chehab","fromEmail":"mchehab@osg.samsung.com","sentAt":"2018-02-12T21:36:17Z","receivedAt":"2018-02-12T21:36:55Z","isPatch":false,"sender":{"key":"mchehab@osg.samsung.com","avatar":null},"body":"Em Mon, 12 Feb 2018 13:15:04 -0800\nLinus Torvalds <torvalds@linux-foundation.org> escreveu:\n\n> On Mon, Feb 12, 2018 at 1:00 PM, Stephen Rothwell <sfr@canb.auug.org.au> wrote:\n> >\n> > Linus, this happens a bit after the merge window, so I am wondering\n> > about the rational of not doing a fast forward merge when merging a\n> > signed tag (I forget the reasoning).  \n> \n> The reasoning is to avoid losing the signature from the tag (when\n> merging a signed tag, the signature gets inserted into the merge\n> commit itself - use \"git log --show-signature\" to see them).\n> \n> So when I merge a signed tag, I do *not* want to fast-forward to the\n> top commit, because then I'd lose the signature from the tag. Thus the\n> \"merging signed tags are non-fast-forward by default\" reasoning.\n> \n> But, yes, that reasoning is really only valid for proper merges of new\n> features, not for back-merges.\n> \n> The problem, of course, is that since git is distributed, git doesn't\n> know who is \"upstream\" and who is \"downstream\", so there's no\n> _technical_ difference between merging a development tree, and a\n> development tree doing a back-merge of the upstream tree.\n> \n> Maybe it was a mistake to make signed tag merges non-fast-forward,\n> since they cause these kinds of issues with people who use \"pull\" to\n> update their otherwise unmodified trees.\n> \n> I can always teach myself to just use --no-ff, since I end up doing\n> things like verifying at the signatures anyway.\n\nHmm... at least at git version 2.14.3, git documentation doesn't\nmention that signed pull requests won't do fast forward. Instead,\nit says that --ff is the default behavior:\n\n\n       --ff\n           When the merge resolves as a fast-forward, only update the branch pointer, without creating a merge commit. This is the\n           default behavior.\n\nBtw, even doing:\n\n\t$ git merge -ff v4.16-rc1\n\nit will still produce a git commit for the merge.\n\n\n-- \nThanks,\nMauro\n"},{"id":"339114","messageId":"xmqqk1vhsxdh.fsf@gitster-ct.c.googlers.com","threadId":"47821","inReplyTo":"CA+55aFwM0vy+pw-Xv=gA19ULMwAXNPhdO3qR5A3hkMrZKJFNSQ@mail.gmail.com","subject":"Re: linux-next: unnecessary merge in the v4l-dvb tree","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-02-12T21:37:14Z","receivedAt":"2018-02-12T21:37:22Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@linux-foundation.org> writes:\n\n> On Mon, Feb 12, 2018 at 1:00 PM, Stephen Rothwell <sfr@canb.auug.org.au> wrote:\n>\n> The problem, of course, is that since git is distributed, git doesn't\n> know who is \"upstream\" and who is \"downstream\", so there's no\n> _technical_ difference between merging a development tree, and a\n> development tree doing a back-merge of the upstream tree.\n>\n> Maybe it was a mistake to make signed tag merges non-fast-forward,\n> since they cause these kinds of issues with people who use \"pull\" to\n> update their otherwise unmodified trees.\n>\n> I can always teach myself to just use --no-ff, since I end up doing\n> things like verifying at the signatures anyway.\n>\n> Junio, comments?\n\nI have a slight suspicion that allowing 'pull' to fast-forward even\nwhen merging a signed tag when it is pulling from a configured\ndefault remote for the branch the user is on, and otherwise keeping\nthe current behaviour, would make majority of people from both camps\nhappier, but I also have a strong conviction that it is being too\nclever and making it hard to explain to people to do such a dwim\nthat tries to guess which way is 'upstream'.\n\nAnother clue we _might_ be able to take advantage of is that when\nupstream maintainers merge a signed tag, we do *not* fetch and store\nthe tag from downstream contributers in our local repository (it is\nlikely that we have --no-tags in remote.<name>.tagopt), but when\ndownstream contributers sync from us with \"git pull\", they do fetch\nand store our tags in their local repository.\n\nSo \"git pull $somewhere $tag\" that defaults to \"--ff\" when the tag\ngets stored somewhere in refs/ (or more explicitly, in refs/tags/)\nand defaults to \"--no-ff\" otherwise (i.e. the tag is fetched only to\nbe recorded in the resulting merge, without ever stored in any of\nour refs), might be a good balance.  \n\nAnd it is easy to explain: \"We realize that it was a mistake to\nunconditionally default to --no-ff and we are reverting the default\nto --ff, but with a twist.  When we tell 'pull' to grab a tag, if\nwe do not store it anywhere in our local ref space, that would mean\nthe tag is totally lost if the pull fast-forwards.  That is why we\nstill use --no-ff in such a case.\"\n\n\n"},{"id":"339115","messageId":"CA+55aFzxsNxgKD1uGZQCiib+=+wCMSa0=B+Ye3Zi-u6kpz8Vrg@mail.gmail.com","threadId":"47821","inReplyTo":"CA+55aFwM0vy+pw-Xv=gA19ULMwAXNPhdO3qR5A3hkMrZKJFNSQ@mail.gmail.com","subject":"Re: linux-next: unnecessary merge in the v4l-dvb tree","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2018-02-12T21:37:04Z","receivedAt":"2018-02-12T21:37:38Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"On Mon, Feb 12, 2018 at 1:15 PM, Linus Torvalds\n<torvalds@linux-foundation.org> wrote:\n>\n> The reasoning is to avoid losing the signature from the tag (when\n> merging a signed tag, the signature gets inserted into the merge\n> commit itself - use \"git log --show-signature\" to see them).\n\nI think the commit that actually introduced the behavior was\n\n    fab47d057: merge: force edit and no-ff mode when merging a tag object\n\nback in 2011, so we've had this behavior for a long time. So it's\nprobably not be worth tweaking the behavior any more, and maybe we\nneed to educate people to not update to other peoples state with \"git\npull\".\n\nMaybe we could just tell people to have something like\n\n       git config --global alias.update pull --ff-only\n\nand use that for \"try to update to upstream\".\n\n               Linus\n"},{"id":"339118","messageId":"xmqqfu65sx20.fsf@gitster-ct.c.googlers.com","threadId":"47821","inReplyTo":"CA+55aFzxsNxgKD1uGZQCiib+=+wCMSa0=B+Ye3Zi-u6kpz8Vrg@mail.gmail.com","subject":"Re: linux-next: unnecessary merge in the v4l-dvb tree","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-02-12T21:44:07Z","receivedAt":"2018-02-12T21:44:26Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@linux-foundation.org> writes:\n\n> Maybe we could just tell people to have something like\n>\n>        git config --global alias.update pull --ff-only\n>\n> and use that for \"try to update to upstream\".\n\nI guess our mails crossed.  I admit that I indeed wondered why you\nwere not giving your usual \"downstream shouldn't do pointless pull\nfrom upstream\" briefly but focused too much on how to tweak the\ndefault without thinking through.\n\nBut I wonder why \"update to upstream\" is merging a signed tag in the\nfirst place.  Wouldn't downstream's \"try to keep up with\" pull be\ngrabbing from branch tips, not tags?\n\n\n\n\n"},{"id":"339121","messageId":"CA+55aFwTp8gg70sHXqOgR01Liv5c8nnfUP0yTdwpkh-rg+2EMA@mail.gmail.com","threadId":"47821","inReplyTo":"xmqqfu65sx20.fsf@gitster-ct.c.googlers.com","subject":"Re: linux-next: unnecessary merge in the v4l-dvb tree","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2018-02-12T21:59:27Z","receivedAt":"2018-02-12T21:59:49Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"On Mon, Feb 12, 2018 at 1:44 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>\n> But I wonder why \"update to upstream\" is merging a signed tag in the\n> first place.  Wouldn't downstream's \"try to keep up with\" pull be\n> grabbing from branch tips, not tags?\n\nI'm actually encouraging maintainers to *not* start their work on some\nrandom \"kernel of the day\".\n\nParticularly during the kernel merge window, the upstream master\nbranch can be pretty flaky. It's *not* a good point to start new\ndevelopment on, so if you're a maintainer, you really don't want to\nuse that as the basis for your work for the next merge window.\n\nSo I encourage people to use a stable point for new development, and\nparticularly actual release kernels. The natural way to do that is\nobviously just to create a new branch:\n\n   git checkout -b topicbranch v4.15\n\nand now you have a good new clean branch for your development.\n\nBut clearly we've got a few people who have gotten used to the whole\n\"git pull\" convenience of both fetching and updating.\n\nSome maintainers don't even use topic branches, because their main\nwork is just merging work by subdevelepoers (that goes for my own\ntree: I use topic branches for merging patch queues and to\noccasionally track my own experimental patches, but 99% of the time\nI'm just on \"master\" and obviously pull other peoples branches).\n\nAnd some maintainers end up using multiple repositories as branches\n(the old _original_ git model). Again, you can just use \"git fetch +\ngit reset\", of course, but that's a bit unsafe. In contrast, doing\n\"git pull --ff-only\" is a safe convenient operation that does both the\nfetch and the update to whatever state.\n\nBut you do need that \"--ff-only\" to avoid the merge.\n\n              Linus\n"},{"id":"339126","messageId":"xmqqzi4drczv.fsf@gitster-ct.c.googlers.com","threadId":"47821","inReplyTo":"CA+55aFwTp8gg70sHXqOgR01Liv5c8nnfUP0yTdwpkh-rg+2EMA@mail.gmail.com","subject":"Re: linux-next: unnecessary merge in the v4l-dvb tree","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-02-12T23:42:44Z","receivedAt":"2018-02-12T23:43:04Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@linux-foundation.org> writes:\n\n> And some maintainers end up using multiple repositories as branches\n> (the old _original_ git model). Again, you can just use \"git fetch +\n> git reset\", of course, but that's a bit unsafe. In contrast, doing\n> \"git pull --ff-only\" is a safe convenient operation that does both the\n> fetch and the update to whatever state.\n>\n> But you do need that \"--ff-only\" to avoid the merge.\n\nOK.  I guess it is legit (and semi-sensible) for downstream\ncontributors to \"git pull --ff-only $upstream $release_tag_X\" to\nbring their long-running topic currently based on release X-1 up to\ndate with respect to release X.  It probably makes more sense than\nrebasing on top of release X, even though it makes a lot less sense\nthan merging their topics into release X.\n\nAs you said, pull of a tag that forbids fast-forward by default is\nrather old development (I am kind of surprised that it was so old,\nin v1.7.9), so it may be a bit difficult to transition.\n\nThere is \n\n\t[pull]\n                ff = only\n\nbut pull.ff is quite global, and not good for intermediate level\nmaintainers who pull to integrate work of their downstream (for\nwhich they do want the current \"do not ff, record the tag in a merge\ncommit\" behaviour) and also pull to catch up from their upstream\n(which they want \"ff-when-able\").  They need to control between\nff=only and ff=when-able, depending on whom they are pulling from.\n\nWe may want per-remote equivalent for it, i.e. e.g.\n\n\t[pull]\n\t\tff=false ;# good default for collecting contributions\n\n\t[remote \"torvalds\"] \n\t\tpullFF = only ;# good default for catching up\n\nor something like that, perhaps?\n"},{"id":"339127","messageId":"20180212222157.0a3bd472@vento.lan","threadId":"47821","inReplyTo":"xmqqzi4drczv.fsf@gitster-ct.c.googlers.com","subject":"Re: linux-next: unnecessary merge in the v4l-dvb tree","fromName":"Mauro Carvalho Chehab","fromEmail":"mchehab@osg.samsung.com","sentAt":"2018-02-13T00:21:57Z","receivedAt":"2018-02-13T00:22:29Z","isPatch":false,"sender":{"key":"mchehab@osg.samsung.com","avatar":null},"body":"Em Mon, 12 Feb 2018 15:42:44 -0800\nJunio C Hamano <gitster@pobox.com> escreveu:\n\n> Linus Torvalds <torvalds@linux-foundation.org> writes:\n> \n> > And some maintainers end up using multiple repositories as branches\n> > (the old _original_ git model). Again, you can just use \"git fetch +\n> > git reset\", of course, but that's a bit unsafe. In contrast, doing\n> > \"git pull --ff-only\" is a safe convenient operation that does both the\n> > fetch and the update to whatever state.\n> >\n> > But you do need that \"--ff-only\" to avoid the merge.  \n> \n> OK.  I guess it is legit (and semi-sensible) for downstream\n> contributors to \"git pull --ff-only $upstream $release_tag_X\" to\n> bring their long-running topic currently based on release X-1 up to\n> date with respect to release X.  It probably makes more sense than\n> rebasing on top of release X, even though it makes a lot less sense\n> than merging their topics into release X.\n> \n> As you said, pull of a tag that forbids fast-forward by default is\n> rather old development (I am kind of surprised that it was so old,\n> in v1.7.9), so it may be a bit difficult to transition.\n> \n> There is \n> \n> \t[pull]\n>                 ff = only\n> \n> but pull.ff is quite global, and not good for intermediate level\n> maintainers who pull to integrate work of their downstream (for\n> which they do want the current \"do not ff, record the tag in a merge\n> commit\" behaviour) and also pull to catch up from their upstream\n> (which they want \"ff-when-able\").  They need to control between\n> ff=only and ff=when-able, depending on whom they are pulling from.\n\nYes, that's my pain. I don't want ff only when pulling from others,\nonly when pulling from upstream tree.\n\n> \n> We may want per-remote equivalent for it, i.e. e.g.\n> \n> \t[pull]\n> \t\tff=false ;# good default for collecting contributions\n> \n> \t[remote \"torvalds\"] \n> \t\tpullFF = only ;# good default for catching up\n> \n> or something like that, perhaps?\n\n\nYeah, something like that works. Please notice, however, that what I\nusually do is:\n\n\t$ git remote update torvalds\n\t$ git merge <tag>\n\t  (or git pull . <tag>)\n\nSo, for the above to work, it should store somehow the remote from\nwhere a tag came from.\n\n\n\n\nThe reason is that I keep locally a cache with several tree clones\n(in bare mode) s that I bother enough to cache (linus, -stable, -next),\nas pulling from BR is time consuming, and I want to do it only once\nand use the same \"cache\" for all my git clones.\n\nI have a few git workdirs for my upstream work, but, as a patch\ndeveloper, I also have \"independent\"[1] git repositories.\n\n[1] Due to disk constraints, the clones actually use --shared. So,\nthe common objects are actually stored inside a single tree.\n\nThanks,\nMauro\n"},{"id":"339205","messageId":"xmqqmv0crepg.fsf@gitster-ct.c.googlers.com","threadId":"47821","inReplyTo":"20180212222157.0a3bd472@vento.lan","subject":"Re: linux-next: unnecessary merge in the v4l-dvb tree","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-02-13T17:18:03Z","receivedAt":"2018-02-13T17:18:28Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Mauro Carvalho Chehab <mchehab@osg.samsung.com> writes:\n\n> Yes, that's my pain. I don't want ff only when pulling from others,\n> only when pulling from upstream tree.\n>\n>> \n>> We may want per-remote equivalent for it, i.e. e.g.\n>> \n>> \t[pull]\n>> \t\tff=false ;# good default for collecting contributions\n>> \n>> \t[remote \"torvalds\"] \n>> \t\tpullFF = only ;# good default for catching up\n>> \n>> or something like that, perhaps?\n>\n>\n> Yeah, something like that works. Please notice, however, that what I\n> usually do is:\n>\n> \t$ git remote update torvalds\n> \t$ git merge <tag>\n> \t  (or git pull . <tag>)\n>\n> So, for the above to work, it should store somehow the remote from\n> where a tag came from.\n\nThat makes me wonder if another heuristic I floated earlier is more\nappropriate.  When merging a tag object T, if refs/tags/T exists and\nit is that tag object, then an updated \"merge\" would default to \"--ff\";\notherwise, it would keep the current default of creating a merge even\nwhen we could fast-forward, in order to record that tag T in the\nresulting history.\n\nOf course, end users can use command line options to override such\nheuristics anyway, but if the behaviour based on the new heuristic\nis easy to explain and understand, and covers majority of the use\ncases without command line override, then we might not even need a\nnew configuration mechanism like remove.torvalds.pullFF mentioned\nabove.\n"},{"id":"339207","messageId":"CA+55aFxkYTeY9h=VHFXi=gbXsnsHCRMAVZ9=1_EsFGSqr0sj9g@mail.gmail.com","threadId":"47821","inReplyTo":"xmqqmv0crepg.fsf@gitster-ct.c.googlers.com","subject":"Re: linux-next: unnecessary merge in the v4l-dvb tree","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2018-02-13T17:33:02Z","receivedAt":"2018-02-13T17:33:20Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"On Tue, Feb 13, 2018 at 9:18 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>\n> That makes me wonder if another heuristic I floated earlier is more\n> appropriate.  When merging a tag object T, if refs/tags/T exists and\n> it is that tag object, then an updated \"merge\" would default to \"--ff\";\n> otherwise, it would keep the current default of creating a merge even\n> when we could fast-forward, in order to record that tag T in the\n> resulting history.\n\nOooh. Yes, that sounds like the right thing to do.\n\nSo the \"no fast-forward\" logic would trigger only if the name we used\nfor merging is one of the temporary ones (ie .git/{FETCH,MERGE}_HEAD),\nnot if the mentioned tag is already a normal tag reference.\n\nThen it's very explicitly about \"don't lose the signing information\".\n\nI'd still have to teach people to use \"--only-ff\" if they don't do the\n\"fetch and merge\" model but literally just do  \"git pull upstream\nvX.Y\", but at least the case Mauro describes would automatically just\nDTRT.\n\nMe likey.\n\n           Linus\n"},{"id":"339305","messageId":"xmqqfu63o2xv.fsf@gitster-ct.c.googlers.com","threadId":"47821","inReplyTo":"CA+55aFxkYTeY9h=VHFXi=gbXsnsHCRMAVZ9=1_EsFGSqr0sj9g@mail.gmail.com","subject":"Re: linux-next: unnecessary merge in the v4l-dvb tree","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-02-14T18:12:44Z","receivedAt":"2018-02-14T18:12:55Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@linux-foundation.org> writes:\n\n> On Tue, Feb 13, 2018 at 9:18 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>>\n>> That makes me wonder if another heuristic I floated earlier is more\n>> appropriate.  When merging a tag object T, if refs/tags/T exists and\n>> it is that tag object, then an updated \"merge\" would default to \"--ff\";\n>> otherwise, it would keep the current default of creating a merge even\n>> when we could fast-forward, in order to record that tag T in the\n>> resulting history.\n>\n> Oooh. Yes, that sounds like the right thing to do.\n>\n> So the \"no fast-forward\" logic would trigger only if the name we used\n> for merging is one of the temporary ones (ie .git/{FETCH,MERGE}_HEAD),\n> not if the mentioned tag is already a normal tag reference.\n>\n> Then it's very explicitly about \"don't lose the signing information\".\n>\n> I'd still have to teach people to use \"--only-ff\" if they don't do the\n> \"fetch and merge\" model but literally just do  \"git pull upstream\n> vX.Y\", but at least the case Mauro describes would automatically just\n> DTRT.\n>\n> Me likey.\n\nThe implementation cannot exactly be \"did the user give FETCH_HEAD\nor v4.16-rc1 from the command line?\", because we'd want to catch it\nwhen Mauro says \"git fetch linus && git merge v4.16-rc1\" and behave\nidentically as \"git pull linus v4.16-rc1\" (and the latter internally\ngets turned into \"git merge FETCH_HEAD\").\n\nSo, instead, we read the \"tag\" line from the tag object to learn the\ntagname T, see if refs/tags/T exists and points at that object, to\nsee if we are Mauro who follows your tags, or if we are you who\nfetch and merge contributors' \"for-linus\" signed tag (which I am\nassuming you won't contaminate your refs/tags/ hierarchy with).\n\nThere are a few fallouts in the testsuite if we go this route.  I am\nnot quite decided if I like the approach.\n\n builtin/merge.c          | 42 ++++++++++++++++++++++++++++++++++++++----\n t/t6200-fmt-merge-msg.sh |  2 +-\n t/t7600-merge.sh         |  2 +-\n 3 files changed, 40 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 30264cfd7c..45c7916505 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -33,6 +33,7 @@\n #include \"sequencer.h\"\n #include \"string-list.h\"\n #include \"packfile.h\"\n+#include \"tag.h\"\n \n #define DEFAULT_TWOHEAD (1<<0)\n #define DEFAULT_OCTOPUS (1<<1)\n@@ -1125,6 +1126,42 @@ static struct commit_list *collect_parents(struct commit *head_commit,\n \treturn remoteheads;\n }\n \n+static int merging_a_throwaway_tag(struct commit *commit)\n+{\n+\tconst char *tag_ref;\n+\tstruct object_id oid;\n+\n+\t/* Are we merging a tag? */\n+\tif (!merge_remote_util(commit) ||\n+\t    !merge_remote_util(commit)->obj ||\n+\t    merge_remote_util(commit)->obj->type != OBJ_TAG)\n+\t\treturn 0;\n+\n+\t/*\n+\t * Now we know we are merging a tag object.  Are we downstream\n+\t * and following the tags from upstream?  If so, we must have\n+\t * the tag object pointed at by \"refs/tags/$T\" where $T is the\n+\t * tagname recorded in the tag object.  We want to allow such\n+\t * a \"just to catch up\" merge to fast-forward.\n+\t */\n+\ttag_ref = xstrfmt(\"refs/tags/%s\",\n+\t\t\t  ((struct tag *)merge_remote_util(commit)->obj)->tag);\n+\n+\tif (!read_ref(tag_ref, &oid) &&\n+\t    !oidcmp(&oid, &merge_remote_util(commit)->obj->oid))\n+\t\treturn 0;\n+\n+\t/*\n+\t * Otherwise, we are playing an integrator's role, making a\n+\t * merge with a throw-away tag from a contributor with\n+\t * something like \"git pull $contributor $signed_tag\".\n+\t * We want to forbid such a merge from fast-forwarding\n+\t * by default; otherwise we would not keep the signature\n+\t * anywhere.\n+\t */\n+\treturn 1;\n+}\n+\n int cmd_merge(int argc, const char **argv, const char *prefix)\n {\n \tstruct object_id result_tree, stash, head_oid;\n@@ -1322,10 +1359,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \t\t\t    oid_to_hex(&commit->object.oid));\n \t\tsetenv(buf.buf, merge_remote_util(commit)->name, 1);\n \t\tstrbuf_reset(&buf);\n-\t\tif (fast_forward != FF_ONLY &&\n-\t\t    merge_remote_util(commit) &&\n-\t\t    merge_remote_util(commit)->obj &&\n-\t\t    merge_remote_util(commit)->obj->type == OBJ_TAG)\n+\t\tif (fast_forward != FF_ONLY && merging_a_throwaway_tag(commit))\n \t\t\tfast_forward = FF_NO;\n \t}\n \ndiff --git a/t/t6200-fmt-merge-msg.sh b/t/t6200-fmt-merge-msg.sh\nindex 2e2fb0e957..a54a52aaa4 100755\n--- a/t/t6200-fmt-merge-msg.sh\n+++ b/t/t6200-fmt-merge-msg.sh\n@@ -512,7 +512,7 @@ test_expect_success 'merge-msg with \"merging\" an annotated tag' '\n \n \ttest_when_finished \"git reset --hard\" &&\n \tannote=$(git rev-parse annote) &&\n-\tgit merge --no-commit $annote &&\n+\tgit merge --no-commit --no-ff $annote &&\n \t{\n \t\tcat <<-EOF\n \t\tMerge tag '\\''$annote'\\''\ndiff --git a/t/t7600-merge.sh b/t/t7600-merge.sh\nindex dfde6a675a..28a1c43ca7 100755\n--- a/t/t7600-merge.sh\n+++ b/t/t7600-merge.sh\n@@ -718,7 +718,7 @@ test_expect_success GPG 'merge --no-edit tag should skip editor' '\n \tgit tag -f -s -m \"A newer commit\" signed &&\n \tgit reset --hard c0 &&\n \n-\tEDITOR=false git merge --no-edit signed &&\n+\tEDITOR=false git merge --no-edit --no-ff signed &&\n \tgit rev-parse signed^0 >expect &&\n \tgit rev-parse HEAD^2 >actual &&\n \ttest_cmp expect actual\n\n\n\n"},{"id":"339520","messageId":"xmqqy3jt7ty8.fsf_-_@gitster-ct.c.googlers.com","threadId":"47821","inReplyTo":"xmqqfu63o2xv.fsf@gitster-ct.c.googlers.com","subject":"[PATCH] merge: allow fast-forward when merging a tracked tag","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-02-15T22:45:51Z","receivedAt":"2018-02-15T22:46:01Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Long time ago at fab47d05 (\"merge: force edit and no-ff mode when\nmerging a tag object\", 2011-11-07), \"git merge\" was made to always\ncreate a merge commit when merging a tag, even when the side branch\nbeing merged is a descendant of the current branch.\n\nThis default is good for merges made by upstream maintainers to\nintegrate work signed by downstream contributors, but will leave\npointless no-ff merges when downstream contributors pull a newer\nrelease tag to make their long-running topic branches catch up with\nthe upstream.  When there is no local work left on the topic, such a\nmerge should simply fast-forward to the commit pointed at by the\nrelease tag.\n\nUpdate the default (again) for \"git merge\" that merges a tag object\nto (1) --no-ff (i.e. create a merge commit even when side branch\nfast forwards) if the tag being merged is not at its expected place\nin refs/tags/ hierarchy and (2) --ff (i.e. allow fast-forward update\nwhen able) otherwise.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n\n    > There are a few fallouts in the testsuite if we go this route.  I am\n    > not quite decided if I like the approach.\n\n    This time with a few tests for the new default, and minimum\n    adjustement to the documentation.\n\n Documentation/merge-options.txt |  3 ++-\n builtin/merge.c                 | 42 +++++++++++++++++++++++++++++++++++++----\n t/t6200-fmt-merge-msg.sh        |  2 +-\n t/t7600-merge.sh                | 38 ++++++++++++++++++++++++++++++++++++-\n 4 files changed, 78 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt\nindex 3888c3ff85..63a3fc0954 100644\n--- a/Documentation/merge-options.txt\n+++ b/Documentation/merge-options.txt\n@@ -35,7 +35,8 @@ set to `no` at the beginning of them.\n --no-ff::\n \tCreate a merge commit even when the merge resolves as a\n \tfast-forward.  This is the default behaviour when merging an\n-\tannotated (and possibly signed) tag.\n+\tannotated (and possibly signed) tag that is not stored in\n+\tits natural place in 'refs/tags/' hierarchy.\n \n --ff-only::\n \tRefuse to merge and exit with a non-zero status unless the\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 30264cfd7c..45c7916505 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -33,6 +33,7 @@\n #include \"sequencer.h\"\n #include \"string-list.h\"\n #include \"packfile.h\"\n+#include \"tag.h\"\n \n #define DEFAULT_TWOHEAD (1<<0)\n #define DEFAULT_OCTOPUS (1<<1)\n@@ -1125,6 +1126,42 @@ static struct commit_list *collect_parents(struct commit *head_commit,\n \treturn remoteheads;\n }\n \n+static int merging_a_throwaway_tag(struct commit *commit)\n+{\n+\tconst char *tag_ref;\n+\tstruct object_id oid;\n+\n+\t/* Are we merging a tag? */\n+\tif (!merge_remote_util(commit) ||\n+\t    !merge_remote_util(commit)->obj ||\n+\t    merge_remote_util(commit)->obj->type != OBJ_TAG)\n+\t\treturn 0;\n+\n+\t/*\n+\t * Now we know we are merging a tag object.  Are we downstream\n+\t * and following the tags from upstream?  If so, we must have\n+\t * the tag object pointed at by \"refs/tags/$T\" where $T is the\n+\t * tagname recorded in the tag object.  We want to allow such\n+\t * a \"just to catch up\" merge to fast-forward.\n+\t */\n+\ttag_ref = xstrfmt(\"refs/tags/%s\",\n+\t\t\t  ((struct tag *)merge_remote_util(commit)->obj)->tag);\n+\n+\tif (!read_ref(tag_ref, &oid) &&\n+\t    !oidcmp(&oid, &merge_remote_util(commit)->obj->oid))\n+\t\treturn 0;\n+\n+\t/*\n+\t * Otherwise, we are playing an integrator's role, making a\n+\t * merge with a throw-away tag from a contributor with\n+\t * something like \"git pull $contributor $signed_tag\".\n+\t * We want to forbid such a merge from fast-forwarding\n+\t * by default; otherwise we would not keep the signature\n+\t * anywhere.\n+\t */\n+\treturn 1;\n+}\n+\n int cmd_merge(int argc, const char **argv, const char *prefix)\n {\n \tstruct object_id result_tree, stash, head_oid;\n@@ -1322,10 +1359,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \t\t\t    oid_to_hex(&commit->object.oid));\n \t\tsetenv(buf.buf, merge_remote_util(commit)->name, 1);\n \t\tstrbuf_reset(&buf);\n-\t\tif (fast_forward != FF_ONLY &&\n-\t\t    merge_remote_util(commit) &&\n-\t\t    merge_remote_util(commit)->obj &&\n-\t\t    merge_remote_util(commit)->obj->type == OBJ_TAG)\n+\t\tif (fast_forward != FF_ONLY && merging_a_throwaway_tag(commit))\n \t\t\tfast_forward = FF_NO;\n \t}\n \ndiff --git a/t/t6200-fmt-merge-msg.sh b/t/t6200-fmt-merge-msg.sh\nindex 2e2fb0e957..a54a52aaa4 100755\n--- a/t/t6200-fmt-merge-msg.sh\n+++ b/t/t6200-fmt-merge-msg.sh\n@@ -512,7 +512,7 @@ test_expect_success 'merge-msg with \"merging\" an annotated tag' '\n \n \ttest_when_finished \"git reset --hard\" &&\n \tannote=$(git rev-parse annote) &&\n-\tgit merge --no-commit $annote &&\n+\tgit merge --no-commit --no-ff $annote &&\n \t{\n \t\tcat <<-EOF\n \t\tMerge tag '\\''$annote'\\''\ndiff --git a/t/t7600-merge.sh b/t/t7600-merge.sh\nindex dfde6a675a..6736d8d131 100755\n--- a/t/t7600-merge.sh\n+++ b/t/t7600-merge.sh\n@@ -700,6 +700,42 @@ test_expect_success 'merge --no-ff --edit' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'merge annotated/signed tag w/o tracking' '\n+\ttest_when_finished \"rm -rf dst; git tag -d anno1\" &&\n+\tgit tag -a -m \"anno c1\" anno1 c1 &&\n+\tgit init dst &&\n+\tgit rev-parse c1 >dst/expect &&\n+\t(\n+\t\t# c0 fast-forwards to c1 but because this repository\n+\t\t# is not a \"downstream\" whose refs/tags follows along\n+\t\t# tag from the \"upstream\", this pull defaults to --no-ff\n+\t\tcd dst &&\n+\t\tgit pull .. c0 &&\n+\t\tgit pull .. anno1 &&\n+\t\tgit rev-parse HEAD^2 >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+test_expect_success 'merge annotated/signed tag w/ tracking' '\n+\ttest_when_finished \"rm -rf dst; git tag -d anno1\" &&\n+\tgit tag -a -m \"anno c1\" anno1 c1 &&\n+\tgit init dst &&\n+\tgit rev-parse c1 >dst/expect &&\n+\t(\n+\t\t# c0 fast-forwards to c1 and because this repository\n+\t\t# is a \"downstream\" whose refs/tags follows along\n+\t\t# tag from the \"upstream\", this pull defaults to --ff\n+\t\tcd dst &&\n+\t\tgit remote add origin .. &&\n+\t\tgit pull origin c0 &&\n+\t\tgit fetch origin &&\n+\t\tgit merge anno1 &&\n+\t\tgit rev-parse HEAD >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n test_expect_success GPG 'merge --ff-only tag' '\n \tgit reset --hard c0 &&\n \tgit commit --allow-empty -m \"A newer commit\" &&\n@@ -718,7 +754,7 @@ test_expect_success GPG 'merge --no-edit tag should skip editor' '\n \tgit tag -f -s -m \"A newer commit\" signed &&\n \tgit reset --hard c0 &&\n \n-\tEDITOR=false git merge --no-edit signed &&\n+\tEDITOR=false git merge --no-edit --no-ff signed &&\n \tgit rev-parse signed^0 >expect &&\n \tgit rev-parse HEAD^2 >actual &&\n \ttest_cmp expect actual\n-- \n2.16.1-194-gb2e45c695d\n\n"},{"id":"339523","messageId":"CAPig+cSJKDH=_-hGZLpZUjC4WEyjMRAd2VPgo-Vd+sHpa4jQ-Q@mail.gmail.com","threadId":"47821","inReplyTo":"xmqqy3jt7ty8.fsf_-_@gitster-ct.c.googlers.com","subject":"Re: [PATCH] merge: allow fast-forward when merging a tracked tag","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-02-15T23:34:19Z","receivedAt":"2018-02-15T23:34:25Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Feb 15, 2018 at 5:45 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> [...]\n> Update the default (again) for \"git merge\" that merges a tag object\n> to (1) --no-ff (i.e. create a merge commit even when side branch\n> fast forwards) if the tag being merged is not at its expected place\n> in refs/tags/ hierarchy and (2) --ff (i.e. allow fast-forward update\n> when able) otherwise.\n>\n> Signed-off-by: Junio C Hamano <gitster@pobox.com>\n> ---\n> diff --git a/builtin/merge.c b/builtin/merge.c\n> @@ -1125,6 +1126,42 @@ static struct commit_list *collect_parents(struct commit *head_commit,\n> +static int merging_a_throwaway_tag(struct commit *commit)\n> +{\n> +       const char *tag_ref;\n> +       struct object_id oid;\n> +\n> +       /* Are we merging a tag? */\n> +       if (!merge_remote_util(commit) ||\n> +           !merge_remote_util(commit)->obj ||\n> +           merge_remote_util(commit)->obj->type != OBJ_TAG)\n> +               return 0;\n> +\n> +       /*\n> +        * Now we know we are merging a tag object.  Are we downstream\n> +        * and following the tags from upstream?  If so, we must have\n> +        * the tag object pointed at by \"refs/tags/$T\" where $T is the\n> +        * tagname recorded in the tag object.  We want to allow such\n> +        * a \"just to catch up\" merge to fast-forward.\n> +        */\n> +       tag_ref = xstrfmt(\"refs/tags/%s\",\n> +                         ((struct tag *)merge_remote_util(commit)->obj)->tag);\n\nxstrfmt() allocates a new string...\n\n> +       if (!read_ref(tag_ref, &oid) &&\n> +           !oidcmp(&oid, &merge_remote_util(commit)->obj->oid))\n> +               return 0;\n\n...which is leaked here...\n\n> +\n> +       /*\n> +        * Otherwise, we are playing an integrator's role, making a\n> +        * merge with a throw-away tag from a contributor with\n> +        * something like \"git pull $contributor $signed_tag\".\n> +        * We want to forbid such a merge from fast-forwarding\n> +        * by default; otherwise we would not keep the signature\n> +        * anywhere.\n> +        */\n> +       return 1;\n\n...and here.\n\n> +}\n"},{"id":"339563","messageId":"xmqqa7w87qsn.fsf@gitster-ct.c.googlers.com","threadId":"47821","inReplyTo":"CAPig+cSJKDH=_-hGZLpZUjC4WEyjMRAd2VPgo-Vd+sHpa4jQ-Q@mail.gmail.com","subject":"Re: [PATCH] merge: allow fast-forward when merging a tracked tag","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-02-16T18:06:16Z","receivedAt":"2018-02-16T18:06:23Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Eric Sunshine <sunshine@sunshineco.com> writes:\n\n>> +       tag_ref = xstrfmt(\"refs/tags/%s\",\n>> +                         ((struct tag *)merge_remote_util(commit)->obj)->tag);\n>\n> xstrfmt() allocates a new string...\n>\n>> +       if (!read_ref(tag_ref, &oid) &&\n>> +           !oidcmp(&oid, &merge_remote_util(commit)->obj->oid))\n>> +               return 0;\n>\n> ...which is leaked here...\n>\n>> +\n>> +       /*\n>> +        * Otherwise, we are playing an integrator's role, making a\n>> +        * merge with a throw-away tag from a contributor with\n>> +        * something like \"git pull $contributor $signed_tag\".\n>> +        * We want to forbid such a merge from fast-forwarding\n>> +        * by default; otherwise we would not keep the signature\n>> +        * anywhere.\n>> +        */\n>> +       return 1;\n>\n> ...and here.\n\nOK.  Thanks.\n"},{"id":"339580","messageId":"xmqq7erc62xb.fsf_-_@gitster-ct.c.googlers.com","threadId":"47821","inReplyTo":"xmqqa7w87qsn.fsf@gitster-ct.c.googlers.com","subject":"[PATCH v2] merge: allow fast-forward when merging a tracked tag","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-02-16T21:27:12Z","receivedAt":"2018-02-16T21:27:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Long time ago at fab47d05 (\"merge: force edit and no-ff mode when\nmerging a tag object\", 2011-11-07), \"git merge\" was made to always\ncreate a merge commit when merging a tag, even when the side branch\nbeing merged is a descendant of the current branch.\n\nThis default is good for merges made by upstream maintainers to\nintegrate work signed by downstream contributors, but will leave\npointless no-ff merges when downstream contributors pull a newer\nrelease tag to make their long-running topic branches catch up with\nthe upstream.  When there is no local work left on the topic, such a\nmerge should simply fast-forward to the commit pointed at by the\nrelease tag.\n\nUpdate the default (again) for \"git merge\" that merges a tag object\nto (1) --no-ff (i.e. create a merge commit even when side branch\nfast forwards) if the tag being merged is not at its expected place\nin refs/tags/ hierarchy and (2) --ff (i.e. allow fast-forward update\nwhen able) otherwise.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n\n * As \"git pull linus v4.16-rc1\" does not create refs/tags/v4.16-rc1\n   during fetch, and there is no way to mechanically tell the\n   invocation from \"git pull davem for-linus\" which does not create\n   refs/tags/for-linus, the new default that fast-forwards would\n   kick in only when \"git fetch linus\" followed by possibly other\n   other work and concluded with \"git merge v4.16-rc1\".  So we still\n   need the \"education\" part to ensure that downstream does not make\n   pointless merges with \"git pull --ff-only\".  \n\n   With or without this patch, the other condition that disables the\n   \"when merging a tag, do not allow fast-forward\" default is\n   \"unless --ff-only is given\".  We may want to rethink it.  For\n   example, shouldn't \"git pull --ff linus v4.16-rc1\" enough clue\n   that the user _knows_ that the signature in that release tag will\n   be lost if the current branch has no original development and\n   explicitly _accepts_ fast-forwarding behaviour?  When the current\n   branch may or may not have original development, having to say\n   \"pull --ff-only\" while catching up to the upstream and accepting\n   50% chance of seeing it fail (when we do have some original\n   development hence the update does not fast-forward) smells like a\n   clunky interface.\n\n Documentation/merge-options.txt |  3 ++-\n builtin/merge.c                 | 43 +++++++++++++++++++++++++++++++++++++----\n t/t6200-fmt-merge-msg.sh        |  2 +-\n t/t7600-merge.sh                | 38 +++++++++++++++++++++++++++++++++++-\n 4 files changed, 79 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt\nindex 3888c3ff85..63a3fc0954 100644\n--- a/Documentation/merge-options.txt\n+++ b/Documentation/merge-options.txt\n@@ -35,7 +35,8 @@ set to `no` at the beginning of them.\n --no-ff::\n \tCreate a merge commit even when the merge resolves as a\n \tfast-forward.  This is the default behaviour when merging an\n-\tannotated (and possibly signed) tag.\n+\tannotated (and possibly signed) tag that is not stored in\n+\tits natural place in 'refs/tags/' hierarchy.\n \n --ff-only::\n \tRefuse to merge and exit with a non-zero status unless the\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 30264cfd7c..532522a854 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -33,6 +33,7 @@\n #include \"sequencer.h\"\n #include \"string-list.h\"\n #include \"packfile.h\"\n+#include \"tag.h\"\n \n #define DEFAULT_TWOHEAD (1<<0)\n #define DEFAULT_OCTOPUS (1<<1)\n@@ -1125,6 +1126,43 @@ static struct commit_list *collect_parents(struct commit *head_commit,\n \treturn remoteheads;\n }\n \n+static int merging_a_throwaway_tag(struct commit *commit)\n+{\n+\tchar *tag_ref;\n+\tstruct object_id oid;\n+\tint is_throwaway_tag = 0;\n+\n+\t/* Are we merging a tag? */\n+\tif (!merge_remote_util(commit) ||\n+\t    !merge_remote_util(commit)->obj ||\n+\t    merge_remote_util(commit)->obj->type != OBJ_TAG)\n+\t\treturn is_throwaway_tag;\n+\n+\t/*\n+\t * Now we know we are merging a tag object.  Are we downstream\n+\t * and following the tags from upstream?  If so, we must have\n+\t * the tag object pointed at by \"refs/tags/$T\" where $T is the\n+\t * tagname recorded in the tag object.  We want to allow such\n+\t * a \"just to catch up\" merge to fast-forward.\n+\t *\n+\t * Otherwise, we are playing an integrator's role, making a\n+\t * merge with a throw-away tag from a contributor with\n+\t * something like \"git pull $contributor $signed_tag\".\n+\t * We want to forbid such a merge from fast-forwarding\n+\t * by default; otherwise we would not keep the signature\n+\t * anywhere.\n+\t */\n+\ttag_ref = xstrfmt(\"refs/tags/%s\",\n+\t\t\t  ((struct tag *)merge_remote_util(commit)->obj)->tag);\n+\tif (!read_ref(tag_ref, &oid) &&\n+\t    !oidcmp(&oid, &merge_remote_util(commit)->obj->oid))\n+\t\tis_throwaway_tag = 0;\n+\telse\n+\t\tis_throwaway_tag = 1;\n+\tfree(tag_ref);\n+\treturn is_throwaway_tag;\n+}\n+\n int cmd_merge(int argc, const char **argv, const char *prefix)\n {\n \tstruct object_id result_tree, stash, head_oid;\n@@ -1322,10 +1360,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \t\t\t    oid_to_hex(&commit->object.oid));\n \t\tsetenv(buf.buf, merge_remote_util(commit)->name, 1);\n \t\tstrbuf_reset(&buf);\n-\t\tif (fast_forward != FF_ONLY &&\n-\t\t    merge_remote_util(commit) &&\n-\t\t    merge_remote_util(commit)->obj &&\n-\t\t    merge_remote_util(commit)->obj->type == OBJ_TAG)\n+\t\tif (fast_forward != FF_ONLY && merging_a_throwaway_tag(commit))\n \t\t\tfast_forward = FF_NO;\n \t}\n \ndiff --git a/t/t6200-fmt-merge-msg.sh b/t/t6200-fmt-merge-msg.sh\nindex 2e2fb0e957..a54a52aaa4 100755\n--- a/t/t6200-fmt-merge-msg.sh\n+++ b/t/t6200-fmt-merge-msg.sh\n@@ -512,7 +512,7 @@ test_expect_success 'merge-msg with \"merging\" an annotated tag' '\n \n \ttest_when_finished \"git reset --hard\" &&\n \tannote=$(git rev-parse annote) &&\n-\tgit merge --no-commit $annote &&\n+\tgit merge --no-commit --no-ff $annote &&\n \t{\n \t\tcat <<-EOF\n \t\tMerge tag '\\''$annote'\\''\ndiff --git a/t/t7600-merge.sh b/t/t7600-merge.sh\nindex dfde6a675a..6736d8d131 100755\n--- a/t/t7600-merge.sh\n+++ b/t/t7600-merge.sh\n@@ -700,6 +700,42 @@ test_expect_success 'merge --no-ff --edit' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'merge annotated/signed tag w/o tracking' '\n+\ttest_when_finished \"rm -rf dst; git tag -d anno1\" &&\n+\tgit tag -a -m \"anno c1\" anno1 c1 &&\n+\tgit init dst &&\n+\tgit rev-parse c1 >dst/expect &&\n+\t(\n+\t\t# c0 fast-forwards to c1 but because this repository\n+\t\t# is not a \"downstream\" whose refs/tags follows along\n+\t\t# tag from the \"upstream\", this pull defaults to --no-ff\n+\t\tcd dst &&\n+\t\tgit pull .. c0 &&\n+\t\tgit pull .. anno1 &&\n+\t\tgit rev-parse HEAD^2 >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+test_expect_success 'merge annotated/signed tag w/ tracking' '\n+\ttest_when_finished \"rm -rf dst; git tag -d anno1\" &&\n+\tgit tag -a -m \"anno c1\" anno1 c1 &&\n+\tgit init dst &&\n+\tgit rev-parse c1 >dst/expect &&\n+\t(\n+\t\t# c0 fast-forwards to c1 and because this repository\n+\t\t# is a \"downstream\" whose refs/tags follows along\n+\t\t# tag from the \"upstream\", this pull defaults to --ff\n+\t\tcd dst &&\n+\t\tgit remote add origin .. &&\n+\t\tgit pull origin c0 &&\n+\t\tgit fetch origin &&\n+\t\tgit merge anno1 &&\n+\t\tgit rev-parse HEAD >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n test_expect_success GPG 'merge --ff-only tag' '\n \tgit reset --hard c0 &&\n \tgit commit --allow-empty -m \"A newer commit\" &&\n@@ -718,7 +754,7 @@ test_expect_success GPG 'merge --no-edit tag should skip editor' '\n \tgit tag -f -s -m \"A newer commit\" signed &&\n \tgit reset --hard c0 &&\n \n-\tEDITOR=false git merge --no-edit signed &&\n+\tEDITOR=false git merge --no-edit --no-ff signed &&\n \tgit rev-parse signed^0 >expect &&\n \tgit rev-parse HEAD^2 >actual &&\n \ttest_cmp expect actual\n-- \n2.16.2-246-ga4ee44448f\n\n\n"}]}