User:Mak77

From MozillaWiki
Jump to: navigation, search

Mercurial backout script

This is a work-in-progress script usable to simplify backouts.

UPDATE: Steve Fink ported this to an hg extension called qbackout, you can find it at https://bitbucket.org/sfink/qbackout

WARNING: It will destroy any uncommited changes in the current tree, so be sure to save your work.

On Mac you need to "port install gsed" and replace all sed calls with gsed, due to non-standard implementation of sed.

################################################################################
# backout
#
# Pass all wanted changesets as arguments, using Mercurial REV syntax.
# When the script is done, just "hg qfin -a && hg push"
#
# Examples:
#   * All changesets between b31507c8ca17 and 1f551298a760 (included), plus
#     changeset 4f57f89653df
#     - backout b31507c8ca17:1f551298a760 4f57f89653df
#   * A single changeset
#     - backout tip
#     - backout 4f57f89653df
backout()
{
  if [ $# -lt 1 ]
  then
    echo "Pass me some changeset number!"
    return 1
  fi

  echo "Reverting tree to a clean status."
  hg update -C 2>&1 | (! grep '^abort:') || { echo "Failed to clean update the tree."; return 1; }
  hg qpop -a 2>&1 | (! grep '^abort:') || { echo "Failed to qpop all patches."; return 1; }
  hg pull 2>&1 | (! grep '^abort:') || { echo "Failed to pull."; return 1; }
  hg update default 2>&1 | (! grep '^abort:') || { echo "Failed to update to default."; return 1; }

  echo "Sorting changesets from newer to older."
  local RANGES=""
  for ENTRY in $*
  do
    local RANGE=""

    # Convert everything to ranges, since it's faster to batch them.
    if [[ "$ENTRY" == *:* ]]
    then
      # Ensure correct order in the range, from newer to older.
      local RANGE=$(hg log -r $ENTRY --template '{rev} {node|short}\n' | sort -n -r | cut -d" " -f2)
      local NEWER=$(echo -ne "$RANGE" | sed -n '1p')
      local OLDER=$(echo -ne "$RANGE" | sed -n '$p')
      RANGE="$(hg log -r $NEWER --template '{rev}') $NEWER:$OLDER"
    else
      RANGE=$(hg log -r $ENTRY --template '{rev} {node|short}:{node|short}')
    fi

    if [ "$RANGE" = "" ]
    then
      echo "Changeset not found."
      return 1
    fi

    RANGES="$RANGES\n$RANGE"
  done
  RANGES=$(echo -ne "$RANGES" | sort -n -r | cut -d" " -f2)

  echo "Creating backout patches in mq."
  local MESSAGE="Backout "
  for RANGE in $RANGES
  do
    echo " - $RANGE"
    local CSET=$(expr "$RANGE" : '\(.*\):.*')
    local PARENT=$(hg parents -r $(expr "$RANGE" : '.*:\(.*\)') --template '{node|short}')

    # Check for multiple parents, if so the changeset is a merge.
    if [ ${#PARENT} -gt 12 ]
    then
      echo "Multiple parents found. Backing out merges is not yet supported."
      return 1
    fi

    hg update $CSET 2>&1 | (! grep '^abort:') || { echo "Failed to update to changeset."; return 1; }
    hg revert -a -r $PARENT 2>&1 | (! grep '^abort:') || { echo "Failed to revert to parent changeset."; return 1; }
    hg qdelete $CSET.diff > /dev/null 2>&1
    hg qnew -f $CSET.diff 2>&1 | (! grep '^abort:') || { echo "Failed to create partial backout patch."; return 1; }
    hg qpop 2>&1 | (! grep '^abort:') || { echo "Failed to qpop partial backout patch."; return 1; }
    # Search for a bug number in the commit message.
    MESSAGE="$MESSAGE$(hg log -r $RANGE --template '{node|short} {desc|fill68|firstline}\n' | sed -n -e 's/\([a-z0-9]\{12\}\) \(.*bug.\([0-9]\{5,\}\).*\|.*\)/\1 (bug \3)/ip' | sed 's/ (bug )//' | sed ':a;N;$!ba;s/\n/, /g'),"
  done

  echo "Folding backout patches to backout.diff."
  hg update -r default 2>&1 | (! grep '^abort:') || { echo "Failed to revert to default."; return 1; }
  hg qdelete backout.diff > /dev/null 2>&1
  hg qnew backout.diff -m "$MESSAGE" 2>&1 | (! grep '^abort:') || { echo "Failed to create backout patch."; return 1; }
  for RANGE in $RANGES
  do
    echo " - $RANGE"
    local CSET=$(expr "$RANGE" : '\(.*\):.*')

    # qfold doesn't return a nonzero exit code on failure :(
    if hg qfold $CSET.diff 2>&1 | grep '^abort:'
    then
      echo "Failed to qfold backout patch, possible code conflicts."
      hg qpop -a > /dev/null 2>&1
      hg qdelete backout.diff > /dev/null 2>&1
      hg qdelete $CSET.diff > /dev/null 2>&1
      return 1
    fi
  done

  # Protect against pushing empty backout patches.
  if [ "$(hg log -r qtip --template '{files}')" = "" ]
  then
    echo "Empty backout patch. Giving up."
    hg qpop -a > /dev/null 2>&1
    hg qdelete backout.diff > /dev/null 2>&1
    return 1
  fi

  hg qrefresh -e
  echo -ne "\nOutgoing backout:\n"
  hg outgoing -q --template '{author}\n{desc}\n\n - Modified files:\n{file_mods}\n\n - Removed files:\n{file_dels}\n\n - Added files:\n{file_adds}\n\n'
}

Colorize mach

This is inherited from the original colorize pymake from Dolske, then further improved by Sdwilsh

Requires updated sed (for unbuffered option) from https://bugzilla.mozilla.org/show_bug.cgi?id=373784

See my blog post: http://blog.bonardo.net/2013/03/05/colorize-mach

mach()
{
  # ANSI colors.
  _black=$(echo -e "\E[30m")
  _red=$(echo -e "\E[31m")
  _green=$(echo -e "\E[32m")
  _yellow=$(echo -e "\E[33m")
  _blue=$(echo -e "\E[34m")
  _magenta=$(echo -e "\E[35m")
  _cyan=$(echo -e "\E[36m")
  _white=$(echo -e "\E[37m")
  _bright=$(echo -e "\E[1m")
  _reset=$(echo -e "\E[0m")

  # Shortcuts for quick styling.
  _time=$_green
  _misc=$_bright$_black
  _info=$_cyan
  _result=$_bright$_green
  _warning=$_bright$_yellow
  _error=$_bright$_red
  _leak=$_bright$_magenta

  ./mach $@ 2>&1 | sed -r -u \
    -e "s/^(.+)(:\swarning|warning:\s)(.+)$/$_warning\1\2\3$_reset/i" \
    -e "s/(leaked\s[0-9]+)(.+)$/$_leak\1\2$_reset/i" \
    -e "s/(adding:?|creating library|generating)(.+)$/\1$_info\2$_reset/i" \
    -e "s/(changing directory to:|processing script file:)(.+)$/\1$_info\2$_reset/i" \
    -e "s/^(\s?\S+\s){1}(processing\s)(\S+)$/\1\2$_info\3$_reset/" \
    -e "s/^(.+)(interrupted|\serror)(\s.+)$/$_error\1\2\3$_reset/i" \
    -e "s/^(.?)(finished)(.+)$/$_result\1\2\3$_reset/i" \
    -e "s/^(\s?\S+\s+\-*\s?){1}(\S+)$/\1$_misc\2$_reset/" \
    -e "s/(TEST-PASS|result summary:|passed: [1-9]{1}[0-9]*)/$_result\1$_reset/i" \
    -e "s/(TEST-INFO)/$_info\1$_reset/i" \
    -e "s/(TEST-UNEXPECTED-\S+|failed: [1-9]{1}[0-9]*)/$_error\1$_reset/i" \
    -e "s/^(\s?[0-9]+\:[0-9]+\.[0-9]+\s)/$_time\1$_reset/"
}

Colorize pymake

This is inherited from the original colorize pymake from Dolske, then further improved by Sdwilsh

Requires updated sed from https://bugzilla.mozilla.org/show_bug.cgi?id=373784

The objdir uses target applications, like browser/ I usually work from the topsrcdir

################################################################################
# echoes the objdir
objdir()
{
  echo "$(ls -d obj*)/browser"
}


################################################################################
# build helper. 'build' for full build, 'build path/to/dir' for incremental.
# You can pass additional targets and options like 'build path/to/dir clean'.
# build toolkit/library for libxul
build()
{
  if [ "$OSTYPE" = "msys" ]
  then
    # Colorize pymake.
    # ANSI color codes.
    # 0-7 == black, red, ... white
    # 3_ = set foreground color
    # 4_ = set background color
    ansi_black=$(echo -e "\E[30m")
    ansi_red=$(echo -e "\E[31m")
    ansi_green=$(echo -e "\E[32m")
    ansi_yellow=$(echo -e "\E[33m")
    ansi_blue=$(echo -e "\E[34m")
    ansi_magenta=$(echo -e "\E[35m")
    ansi_cyan=$(echo -e "\E[36m")
    ansi_white=$(echo -e "\E[37m")
    # make colors brighter
    ansi_bright=$(echo -e "\E[1m")
    # return to terminal default
    ansi_reset=$(echo -e "\E[0m")
  fi

  if [ $# -gt 0 ]
  then
    what2build="-C $(objdir)/$@"
  else
    what2build="-f client.mk"
  fi
  
  if [ "$OSTYPE" = "msys" ]
  then
    python -OO build/pymake/make.py -j12 $what2build 2>&1 | sed -r -u \
      -e "s/(TEST-UNEXPECTED-[A-Z]+.*)$/$ansi_bright$ansi_red\1$ansi_reset/" \
      -e "s/(TEST-PASS)/$ansi_green\1$ansi_reset/" \
      -e "s/(TEST-INFO)/$ansi_cyan\1$ansi_reset/" \
      -e "s/(.+)(return code)(.+)/$ansi_red\1$ansi_bright\2\3$ansi_reset/" \
      -e "s/^([a-z]\:\\\.+(\.exe|\.py).+)/$ansi_bright$ansi_black\1$ansi_reset/" \
      -e "s/^(\s+.+)/$ansi_bright$ansi_black\1$ansi_reset/" \
      -e "s/(Entering directory )(.+)/\1$ansi_cyan\2$ansi_reset/" \
      -e "s/(Leaving directory )(.+)/\1$ansi_bright$ansi_black\2$ansi_reset/" \
      -e "s/(evaluation from )(.+)/\1$ansi_bright$ansi_black\2$ansi_reset/" \
      -e "s/(.+ \: )(warning .+)/$ansi_yellow\1$ansi_bright\2$ansi_reset/" \
      -e "s/(.+ \: )((fatal )?error .+)/$ansi_red\1$ansi_bright\2$ansi_reset/" \
      -e "s/(.+\: )(Found error)/\1$ansi_red$ansi_bright\2$ansi_reset/" \
      -e "s/(No rule to make target .+)/$ansi_red$ansi_bright\1$ansi_reset/"
  else
    make -j12 $what2build
  fi

  cd $(hg root)
}