view contrib/bash_completion @ 43198:c16fe77e340a

pathcopies: give up any optimization based on `introrev` Between 8a0136f69027 and d98fb3f42f33, we sped up the search for the introduction revision during path copies. However, further checking show that finding the introduction revision is still expensive and that we are better off without it. So we simply drop it and only rely on the linkrev optimisation. I ran `perfpathcopies` on 6989 pair of revision in the pypy repository (`hg perfhelper-pathcopies`. The result is massively in favor of dropping this condition. The result of the copy tracing are unchanged. Attempt to use a smaller changes preserving linkrev usage were unsuccessful, it can return wrong result. The following changesets broke test-mv-cp-st-diff.t - if not f.isintroducedafter(limit): + if limit >= 0 and f.linkrev() < limit: return None Here are various numbers (before this changeset/after this changesets) source destination before after saved-time ratio worth cases e66f24650daf 695dfb0f493b 1.062843 1.246369 -0.183526 1.172675 c979853a3b6a 8d60fe293e79 1.036985 1.196414 -0.159429 1.153743 22349fa2fc33 fbb1c9fd86c0 0.879926 1.038682 -0.158756 1.180420 682b98f3e672 a4878080a536 0.909952 1.063801 -0.153849 1.169074 5adabc9b9848 920958a93997 0.993622 1.147452 -0.153830 1.154817 worse 1% dbfbfcf077e9 aea8f2fd3593 1.016595 1.082999 -0.066404 1.065320 worse 5% c95f1ced15f2 7d29d5e39734 0.453694 0.471156 -0.017462 1.038488 worse 10% 3e144ed1d5b7 2aef0e942480 0.035140 0.037535 -0.002395 1.068156 worse 25% 321fc60db035 801748ba582a 0.009267 0.009325 -0.000058 1.006259 median 2088ce763fc2 e6991321d78b 0.000665 0.000651 0.000014 0.978947 best 25% 915631a97de6 385b31354be6 0.040743 0.040363 0.000380 0.990673 best 10% ad495c36a765 19c10384d3e7 0.431658 0.411490 0.020168 0.953278 best 5% d13ae7d283ae 813c99f810ac 1.141404 1.075346 0.066058 0.942126 best 1% 81593cb4a496 99ae11866969 1.833297 0.063823 1.769474 0.034813 best cases c3b14617fbd7 743a0fcaa4eb 1101.811740 2.735970 1099.075770 0.002483 c3b14617fbd7 9ba6ab77fd29 1116.753953 2.800729 1113.953224 0.002508 058b99d6e81f 57e249b7a3ea 1246.128485 3.042762 1243.085723 0.002442 9a8c361aab49 0354a250d371 1253.111894 3.085796 1250.026098 0.002463 442dbbc53c68 3ec1002a818c 1261.786294 3.138607 1258.647687 0.002487 As one can see, the average case is not really impacted. However, the worth case we get after this changeset are much better than the one we had before it. We have 30 pairs where improvements are above 10 minutes. This reflect in the combined time for all pairs before: 26256s after: 1300s (-95%) If we remove these pathological 30 cases, we still see a significant improvements: before: 1631s after: 1245s (-24%)
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Thu, 10 Oct 2019 03:49:33 +0200
parents c54d4607e8aa
children f71c8eea7161
line wrap: on
line source

# bash completion for the Mercurial distributed SCM -*- sh -*-

# Docs:
#
# If you source this file from your .bashrc, bash should be able to
# complete a command line that uses hg with all the available commands
# and options and sometimes even arguments.
#
# Mercurial allows you to define additional commands through extensions.
# Bash should be able to automatically figure out the name of these new
# commands and their options.  See below for how to define _hg_opt_foo
# and _hg_cmd_foo functions to fine-tune the completion for option and
# non-option arguments, respectively.
#
#
# Notes about completion for specific commands:
#
# - the completion function for the email command from the patchbomb
#   extension will try to call _hg_emails to get a list of e-mail
#   addresses.  It's up to the user to define this function.  For
#   example, put the addresses of the lists that you usually patchbomb
#   in ~/.patchbomb-to and the addresses that you usually use to send
#   the patchbombs in ~/.patchbomb-from and use something like this:
#
#      _hg_emails()
#      {
#          if [ -r ~/.patchbomb-$1 ]; then
#              cat ~/.patchbomb-$1
#          fi
#      }
#
#
# Writing completion functions for additional commands:
#
# If it exists, the function _hg_cmd_foo will be called without
# arguments to generate the completion candidates for the hg command
# "foo".  If the command receives some arguments that aren't options
# even though they start with a "-", you can define a function called
# _hg_opt_foo to generate the completion candidates.  If _hg_opt_foo
# doesn't return 0, regular completion for options is attempted.
#
# In addition to the regular completion variables provided by bash,
# the following variables are also set:
# - $hg - the hg program being used (e.g. /usr/bin/hg)
# - $cmd - the name of the hg command being completed
# - $cmd_index - the index of $cmd in $COMP_WORDS
# - $cur - the current argument being completed
# - $prev - the argument before $cur
# - $global_args - "|"-separated list of global options that accept
#                  an argument (e.g. '--cwd|-R|--repository')
# - $canonical - 1 if we canonicalized $cmd before calling the function
#                0 otherwise
#

shopt -s extglob

_hg_cmd()
{
    HGPLAIN=1 "$hg" "$@" 2>/dev/null
}

_hg_commands()
{
    local commands
    commands="$(HGPLAINEXCEPT=alias _hg_cmd debugcomplete "$cur")" || commands=""
    COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$commands' -- "$cur"))
}

_hg_paths()
{
    local paths="$(_hg_cmd paths -q)"
    COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$paths' -- "$cur"))
}

_hg_repos()
{
    local i
    for i in $(compgen -d -- "$cur"); do
        test ! -d "$i"/.hg || COMPREPLY=(${COMPREPLY[@]:-} "$i")
    done
}

_hg_debugpathcomplete()
{
    local files="$(_hg_cmd debugpathcomplete $1 "$cur")"
    local IFS=$'\n'
    COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$files' -- "$cur"))
}

_hg_status()
{
    if [ -z "$HGCOMPLETE_NOSTATUS" ]; then
        local files="$(_hg_cmd status -n$1 "glob:$cur**")"
        local IFS=$'\n'
        COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$files' -- "$cur"))
    fi
}

_hg_branches()
{
    local branches="$(_hg_cmd branches -q)"
    local IFS=$'\n'
    COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$branches' -- "$cur"))
}

_hg_bookmarks()
{
    local bookmarks="$(_hg_cmd bookmarks -q)"
    local IFS=$'\n'
    COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$bookmarks' -- "$cur"))
}

_hg_labels()
{
    local labels="$(_hg_cmd debugnamecomplete "$cur")"
    local IFS=$'\n'
    COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$labels' -- "$cur"))
}

# this is "kind of" ugly...
_hg_count_non_option()
{
    local i count=0
    local filters="$1"

    for ((i=1; $i<=$COMP_CWORD; i++)); do
        if [[ "${COMP_WORDS[i]}" != -* ]]; then
            if [[ ${COMP_WORDS[i-1]} == @($filters|$global_args) ]]; then
                continue
            fi
            count=$(($count + 1))
        fi
    done

    echo $(($count - 1))
}

_hg_fix_wordlist()
{
    local LASTCHAR=' '
    if [ ${#COMPREPLY[@]} = 1 ]; then
        [ -d "$COMPREPLY" ] && LASTCHAR=/
        COMPREPLY=$(printf %q%s "$COMPREPLY" "$LASTCHAR")
    else
        for ((i=0; i < ${#COMPREPLY[@]}; i++)); do
            [ -d "${COMPREPLY[$i]}" ] && COMPREPLY[$i]=${COMPREPLY[$i]}/
        done
    fi
}

_hg()
{
    local cur prev cmd cmd_index opts i aliashg
    # global options that receive an argument
    local global_args='--cwd|-R|--repository|--color|--config|--encoding|--encodingmode|--pager'
    local hg="$1"
    local canonical=0

    aliashg=$(alias $hg 2>/dev/null)
    if [[ -n "$aliashg" ]]; then
      aliashg=${aliashg#"alias $hg='"}
      aliashg=${aliashg%"'"}
      hg=$aliashg
    fi

    COMPREPLY=()
    cur="$2"
    prev="$3"

    # searching for the command
    # (first non-option argument that doesn't follow a global option that
    #  receives an argument)
    for ((i=1; $i<=$COMP_CWORD; i++)); do
        if [[ ${COMP_WORDS[i]} != -* ]]; then
            if [[ ${COMP_WORDS[i-1]} != @($global_args) ]]; then
                cmd="${COMP_WORDS[i]}"
                cmd_index=$i
                break
            fi
        fi
    done

    if [[ "$cur" == -* ]]; then
        if [ "$(type -t "_hg_opt_$cmd")" = function ] && "_hg_opt_$cmd"; then
            _hg_fix_wordlist
            return
        fi

        opts=$(HGPLAINEXCEPT=alias _hg_cmd debugcomplete --options "$cmd")

        COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$opts' -- "$cur"))
        _hg_fix_wordlist
        return
    fi

    # global options
    case "$prev" in
        -R|--repository)
            _hg_paths
            _hg_repos
            _hg_fix_wordlist
            return
        ;;
        --cwd)
            # Stick with default bash completion
            _hg_fix_wordlist
            return
        ;;
        --color)
            local choices='true false yes no always auto never debug'
            COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$choices' -- "$cur"))
            _hg_fix_wordlist
            return
        ;;
        --pager)
            local choices='true false yes no always auto never'
            COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$choices' -- "$cur"))
            _hg_fix_wordlist
            return
        ;;
    esac

    if [ -z "$cmd" ] || [ $COMP_CWORD -eq $i ]; then
        _hg_commands
        _hg_fix_wordlist
        return
    fi

    # try to generate completion candidates for whatever command the user typed
    local help
    if _hg_command_specific; then
        _hg_fix_wordlist
        return
    fi

    # canonicalize the command name and try again
    help=$(_hg_cmd help "$cmd")
    if [ $? -ne 0 ]; then
        # Probably either the command doesn't exist or it's ambiguous
        return
    fi
    cmd=${help#hg }
    cmd=${cmd%%[$' \n']*}
    canonical=1
    _hg_command_specific
    _hg_fix_wordlist
}

_hg_command_specific()
{
    if [ "$(type -t "_hg_cmd_$cmd")" = function ]; then
        "_hg_cmd_$cmd"
        return 0
    fi

    if [ "$cmd" != status ]; then
        case "$prev" in
            -r|--rev)
                if [[ $canonical = 1 || status != "$cmd"* ]]; then
                    _hg_labels
                    return 0
                fi
                return 1
            ;;
            -B|--bookmark)
                if [[ $canonical = 1 || status != "$cmd"* ]]; then
                    _hg_bookmarks
                    return 0
                fi
                return 1
            ;;
            -b|--branch)
                if [[ $canonical = 1 || status != "$cmd"* ]]; then
                    _hg_branches
                    return 0
                fi
                return 1
            ;;
        esac
    fi

    local aliascmd=$(_hg_cmd showconfig alias.$cmd | awk '{print $1}')
    [ -n "$aliascmd" ] && cmd=$aliascmd

    case "$cmd" in
        help)
            _hg_commands
        ;;
        export)
            if _hg_ext_mq_patchlist qapplied && [ "${COMPREPLY[*]}" ]; then
                return 0
            fi
            _hg_labels
        ;;
        manifest|update|up|checkout|co)
            _hg_labels
        ;;
        pull|push|outgoing|incoming)
            _hg_paths
            _hg_repos
        ;;
        paths)
            _hg_paths
        ;;
        add)
            _hg_status "u"
        ;;
        merge)
            _hg_labels
        ;;
        commit|ci|record|amend)
            _hg_status "mar"
        ;;
        remove|rm)
            _hg_debugpathcomplete -n
        ;;
        forget)
            _hg_debugpathcomplete -fa
        ;;
        diff)
            _hg_status "mar"
        ;;
        revert)
            _hg_status "mard"
        ;;
        clone)
            local count=$(_hg_count_non_option)
            if [ $count = 1 ]; then
                _hg_paths
            fi
            _hg_repos
        ;;
        debugindex|debugindexdot)
            COMPREPLY=(${COMPREPLY[@]:-} $(compgen -f -X "!*.i" -- "$cur"))
        ;;
        debugdata)
            COMPREPLY=(${COMPREPLY[@]:-} $(compgen -f -X "!*.d" -- "$cur"))
        ;;
        *)
            return 1
        ;;
    esac

    return 0
}

complete -o bashdefault -o default -o nospace -F _hg hg \
    || complete -o default -o nospace -F _hg hg


# Completion for commands provided by extensions

# bookmarks
_hg_cmd_bookmarks()
{
    _hg_bookmarks
    return
}

# mq
_hg_ext_mq_patchlist()
{
    local patches
    patches=$(_hg_cmd $1)
    if [ $? -eq 0 ] && [ "$patches" ]; then
        COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$patches' -- "$cur"))
        return 0
    fi
    return 1
}

_hg_ext_mq_queues()
{
    local root=$(_hg_cmd root)
    local n
    for n in $(cd "$root"/.hg && compgen -d -- "$cur"); do
        # I think we're usually not interested in the regular "patches" queue
        # so just filter it.
        if [ "$n" != patches ] && [ -e "$root/.hg/$n/series" ]; then
            COMPREPLY=(${COMPREPLY[@]:-} "$n")
        fi
    done
}

_hg_cmd_qpop()
{
    if [[ "$prev" = @(-n|--name) ]]; then
        _hg_ext_mq_queues
        return
    fi
    _hg_ext_mq_patchlist qapplied
}

_hg_cmd_qpush()
{
    if [[ "$prev" = @(-n|--name) ]]; then
        _hg_ext_mq_queues
        return
    fi
    _hg_ext_mq_patchlist qunapplied
}

_hg_cmd_qgoto()
{
    if [[ "$prev" = @(-n|--name) ]]; then
        _hg_ext_mq_queues
        return
    fi
    _hg_ext_mq_patchlist qseries
}

_hg_cmd_qdelete()
{
    local qcmd=qunapplied
    if [[ "$prev" = @(-r|--rev) ]]; then
        qcmd=qapplied
    fi
    _hg_ext_mq_patchlist $qcmd
}

_hg_cmd_qfinish()
{
    if [[ "$prev" = @(-a|--applied) ]]; then
        return
    fi
    _hg_ext_mq_patchlist qapplied
}

_hg_cmd_qsave()
{
    if [[ "$prev" = @(-n|--name) ]]; then
        _hg_ext_mq_queues
        return
    fi
}

_hg_cmd_rebase() {
   if [[ "$prev" = @(-s|--source|-d|--dest|-b|--base|-r|--rev) ]]; then
       _hg_labels
       return
   fi
}

_hg_cmd_strip()
{
    if [[ "$prev" = @(-B|--bookmark) ]]; then
        _hg_bookmarks
        return
    fi
    _hg_labels
}

_hg_cmd_qcommit()
{
    local root=$(_hg_cmd root)
    # this is run in a sub-shell, so we can't use _hg_status
    local files=$(cd "$root/.hg/patches" && _hg_cmd status -nmar)
    COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$files' -- "$cur"))
}

_hg_cmd_qfold()
{
    _hg_ext_mq_patchlist qunapplied
}

_hg_cmd_qrename()
{
    _hg_ext_mq_patchlist qseries
}

_hg_cmd_qheader()
{
    _hg_ext_mq_patchlist qseries
}

_hg_cmd_qclone()
{
    local count=$(_hg_count_non_option)
    if [ $count = 1 ]; then
        _hg_paths
    fi
    _hg_repos
}

_hg_ext_mq_guards()
{
    _hg_cmd qselect --series | sed -e 's/^.//'
}

_hg_cmd_qselect()
{
    local guards=$(_hg_ext_mq_guards)
    COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$guards' -- "$cur"))
}

_hg_cmd_qguard()
{
    local prefix=''

    if [[ "$cur" == +* ]]; then
        prefix=+
    elif [[ "$cur" == -* ]]; then
        prefix=-
    fi
    local ncur=${cur#[-+]}

    if ! [ "$prefix" ]; then
        _hg_ext_mq_patchlist qseries
        return
    fi

    local guards=$(_hg_ext_mq_guards)
    COMPREPLY=(${COMPREPLY[@]:-} $(compgen -P $prefix -W '$guards' -- "$ncur"))
}

_hg_opt_qguard()
{
    local i
    for ((i=cmd_index+1; i<=COMP_CWORD; i++)); do
        if [[ ${COMP_WORDS[i]} != -* ]]; then
            if [[ ${COMP_WORDS[i-1]} != @($global_args) ]]; then
                _hg_cmd_qguard
                return 0
            fi
        elif [ "${COMP_WORDS[i]}" = -- ]; then
            _hg_cmd_qguard
            return 0
        fi
    done
    return 1
}

_hg_cmd_qqueue()
{
    local q
    local queues
    local opts="--list --create --rename --delete --purge"

    queues=$( _hg_cmd qqueue --quiet )

    COMPREPLY=( $( compgen -W "${opts} ${queues}" "${cur}" ) )
}


# hbisect
_hg_cmd_bisect()
{
    local i subcmd

    # find the sub-command
    for ((i=cmd_index+1; i<=COMP_CWORD; i++)); do
        if [[ ${COMP_WORDS[i]} != -* ]]; then
            if [[ ${COMP_WORDS[i-1]} != @($global_args) ]]; then
                subcmd="${COMP_WORDS[i]}"
                break
            fi
        fi
    done

    if [ -z "$subcmd" ] || [ $COMP_CWORD -eq $i ] || [ "$subcmd" = help ]; then
        COMPREPLY=(${COMPREPLY[@]:-}
                   $(compgen -W 'bad good help init next reset' -- "$cur"))
        return
    fi

    case "$subcmd" in
        good|bad)
            _hg_labels
            ;;
    esac

    return
}


# patchbomb
_hg_cmd_email()
{
    case "$prev" in
        -c|--cc|-t|--to|-f|--from|--bcc)
            # we need an e-mail address. let the user provide a function
            # to get them
            if [ "$(type -t _hg_emails)" = function ]; then
                local arg=to
                if [[ "$prev" == @(-f|--from) ]]; then
                    arg=from
                fi
                local addresses=$(_hg_emails $arg)
                COMPREPLY=(${COMPREPLY[@]:-}
                           $(compgen -W '$addresses' -- "$cur"))
            fi
            return
            ;;
        -m|--mbox)
            # fallback to standard filename completion
            return
            ;;
        -s|--subject)
            # free form string
            return
            ;;
    esac

    _hg_labels
    return
}


# gpg
_hg_cmd_sign()
{
    _hg_labels
}


# transplant
_hg_cmd_transplant()
{
    case "$prev" in
        -s|--source)
            _hg_paths
            _hg_repos
            return
            ;;
        --filter)
            # standard filename completion
            return
            ;;
    esac

    # all other transplant options values and command parameters are revisions
    _hg_labels
    return
}

# shelve
_hg_shelves()
{
    local shelves="$(_hg_cmd shelve -ql)"
    local IFS=$'\n'
    COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$shelves' -- "$cur"))
}

_hg_cmd_shelve()
{
    if [[ "$prev" = @(-d|--delete|-l|--list|-p|--patch|--stat) ]]; then
        _hg_shelves
    else
        _hg_status "mard"
    fi
}

_hg_cmd_unshelve()
{
    _hg_shelves
}