git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH] branch & tag: Add a --no-contains option

From
Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Date
Mar 9, 2017, 10:41 UTC
Message-ID
<CACBZZX53rMiB5-cA_7-SeU2Dt7d_Cr7_GgyC0rjQQPPf4qyCqw@mail.gmail.com>
In-Reply-To
<20170309100910.z4h7bwqzxw2xynyu@sigill.intra.peff.net>
On Thu, Mar 9, 2017 at 11:09 AM, Jeff King <peff@peff.net> wrote:
Show 27 quoted lines
> On Wed, Mar 08, 2017 at 08:20:25PM +0000, Ævar Arnfjörð Bjarmason wrote:
>
>> Change the branch & tag commands to have a --no-contains option in
>> addition to their longstanding --contains options.
>>
>> The use-case I have for this is mainly to find the last-good rollout
>> tag given a known-bad <commit>. Right given a hypothetically bad
>> commit v2.10.1-3-gcf5c7253e0 now you can find that with this hacky
>> one-liner:
>>
>>     (./git tag -l 'v[0-9]*'; ./git tag -l 'v[0-9]*' --contains v2.10.1-3-gcf5c7253e0)|sort|uniq -c|grep -E '^ *1 '|awk '{print $2}'
>>
>> But with the --no-contains option you can now get the exact same
>> output with:
>>
>>     ./git tag -l 'v[0-9]*' --no-contains v2.10.1-3-gcf5c7253e0 | sort
>
> I think that's a good goal.
>
> I'm not sure about the name. I would have expected "--no-contains" to
> reset the list of "--contains" commits to the empty set. That's an
> option convention we've been slowly moving towards (e.g., with
> OPT_STRING_LIST).
>
> What you've added here _does_ match "--no-merged", though. I'm not sure
> of the best way forward. At the very least, "--no-contains" is currently
> an error, so you would not be changing existing behavior.

I initially started hacking this up as --not-contains, but after briefly chatting with Christian about it off-list he suggested --no-*. Since as you point out it's consistent with --no-merge. I have no strong view on it, I just want the feature whatever the flag is called.

Show 8 quoted lines
>> Once I'd implemented this for "tag" it was easy enough to add it for
>> "branch". I haven't added it to "for-each-ref" but that would be
>> trivial if anyone cares, but that use-case would be even more obscure
>> than adding it to "branch", so I haven't bothered.
>
> I'd prefer to have it consistently in all three. We should be able to
> tell people to use for-each-ref in their scripts, and that's harder if
> it is missing features.

Agreed. I'd already hacked that up this morning for a v2. It works & has tests at https://github.com/avar/git/tree/avar/no-contains-2

Show 5 quoted lines
>> The "describe" command also has a --contains option, but its semantics
>> are unrelated to what tag/branch/for-each-ref use --contains for, and
>> I don't see how a --no-contains option for it would make any sense.
>
> Yeah, I think that feature is orthogonal.

*Nod* just adding a note about it in case anyone's puzzled about why describe doesn't have --no-contains, elaborated & clarified this a bit in my WIP v2.

Show 25 quoted lines
>> -static int commit_contains(struct ref_filter *filter, struct commit *commit)
>> +static int commit_contains(struct ref_filter *filter, struct commit *commit, const int with_commit)
>>  {
>> +     struct commit_list *tmp = with_commit ? filter->with_commit : filter->no_commit;
>>       if (filter->with_commit_tag_algo)
>> -             return contains_tag_algo(commit, filter->with_commit);
>> -     return is_descendant_of(commit, filter->with_commit);
>> +             return contains_tag_algo(commit, tmp);
>> +     return is_descendant_of(commit, tmp);
>>  }
>
> Perhaps it would be simpler if the caller just passed the right
> commit_list rather than a flag. We unfortunately do still need to pass
> the "filter" (for the algorithm field), but the caller is then:
>
>   if (filter->with_commit &&
>       !commit_contains(filter, filter->with_commit, commit))
>           return 0;
>   if (filter->no_commit &&
>       commit_contains(filter, filter->no_commit, commit))
>           return 0;
>
> which avoids the 0/1 flag whose meaning is not immediately apparent at
> the callsite. One day we can hopefully unify the two algorithms and
> ditch the extra filter parameter.

My C rustyness is showing. Yeah that's much better, thanks, changed it to that in my WIP v2.

Show 5 quoted lines
> I almost suggested that there simply be an option to invert the match
> (like --invert-contains or something).  But what you have here is more
> flexible, if somebody ever wanted to do:
>
>   git tag --contains X --no-contains Y

Yeah that's really useful. E.g. this shows the branches I branched off (or have locally) from 2.6..2.8:

    $ ./git branch --contains v2.6.0 --no-contains v2.8.0
      avar/monkeypatch-untracked-cache-disabled
      avar/uc-notifs21
      dturner/pclouds-watchman-noshm
But I'd expect this to show all the tags between the two:
    $ ./git tag --contains v2.6.0 --no-contains v2.8.0
    $

But it just returns an empty list. Manually disabling the contains_tag_algo() path (i.e. effectively locally reverting your ffc4b8012d) makes it "work", but of course it's much slower now. I haven't dug into why it's not working yet.

Also I wonder if this should be an error:
    $ ./git [tag|branch|for-each-ref] --contains A --no-contains A

I.e. when you give the same argument to both, this can never return anything for obvious reasons.

Show 13 quoted lines
>> @@ -1708,8 +1782,91 @@ run_with_limited_stack () {
>>
>>  test_lazy_prereq ULIMIT_STACK_SIZE 'run_with_limited_stack true'
>>
>> +# These are all the tags we've created above
>> +cat >expect.no-contains <<EOF
>> [...80 tags...]
>> +EOF
>
> That's a lot of tags, and I'd worry it makes the test a little brittle.
> Can we limit the set of tags with a name-match? It shouldn't affect the
> purpose of the test (the deep stack comes from traversing the commits,
> not the number of tags).

I'll make this less sucky in v2 somehow. I did it this way because no existing test was checking all the tags we'd created at the end, so this does that by proxy now, but I agree it's too verbose. Will fix it.

Previous: Jeff KingNext: Jeff King
Message 4 of 28 in “branch & tag: Add a --no-contains option”
  1. branch & tag: Add a --no-contains optionÆvar Arnfjörð Bjarmason, Mar 8, 2017
  2. Junio C HamanoMar 8, 2017
  3. Jeff KingMar 9, 2017
  4. Ævar Arnfjörð BjarmasonMar 9, 2017
  5. Jeff KingMar 9, 2017
  6. Ævar Arnfjörð BjarmasonMar 9, 2017
  7. Jeff KingMar 9, 2017
  8. 0/4 fix object flag pollution in "tag --contains"Jeff King, Mar 9, 2017
  9. 1/4 ref-filter: move ref_cbdata definition into ref-filter.cJeff King, Mar 9, 2017
  10. 2/4 ref-filter: use contains_result enum consistentlyJeff King, Mar 9, 2017
  11. 3/4 ref-filter: die on parse_commit errorsJeff King, Mar 9, 2017
  12. 4/4 ref-filter: use separate cache for contains_tag_algoJeff King, Mar 9, 2017
  13. Ævar Arnfjörð BjarmasonMar 11, 2017
  14. Ævar Arnfjörð BjarmasonMar 11, 2017
  15. Jeff KingMar 12, 2017
  16. Ævar Arnfjörð BjarmasonMar 11, 2017
  17. ref-filter: Add --no-contains option to tag/branch/for-each-refÆvar Arnfjörð Bjarmason, Mar 11, 2017
  18. Junio C HamanoMar 12, 2017
  19. Ævar Arnfjörð BjarmasonMar 12, 2017
  20. Junio C HamanoMar 12, 2017
  21. Ævar Arnfjörð BjarmasonMar 9, 2017
  22. Jeff KingMar 9, 2017
  23. Ævar Arnfjörð BjarmasonMar 10, 2017
  24. ref-filter: Add --no-contains option to tag/branch/for-each-refÆvar Arnfjörð Bjarmason, Mar 9, 2017
  25. Christian CouderMar 9, 2017
  26. Ævar Arnfjörð BjarmasonMar 10, 2017
  27. Ævar Arnfjörð BjarmasonMar 10, 2017
  28. ref-filter: Add --no-contains option to tag/branch/for-each-refÆvar Arnfjörð Bjarmason, Mar 10, 2017

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.