Repository: mozilla/bugbug

Issue:
Add number of backouts as a feature



Discussion:
Hi Marco! From [here](https://www.mercurial-scm.org/wiki/Backout), I can suggest that the number of backouts is equal to number of parents in the changesets' info. Am I correct? 
No, all changesets have a parent, even those which were never backed out.

For example, take a look at https://hg.mozilla.org/mozilla-central/rev/758c71218a1f. It has one parent, and it has not been backed out.

https://hg.mozilla.org/mozilla-central/rev/7065c7171760 has one parent too, and it has been backed out by https://hg.mozilla.org/mozilla-central/rev/2b73321e629d.

There are a few possible ways to implement this:
1) Figuring out if we can get the backout information directly using hg.log and a template (https://github.com/mozilla/version-control-tools/blob/d5db0980ab004b3aada29afdcf798a8a95520240/hgext/mozext/__init__.py#L137, https://github.com/mozilla/version-control-tools/blob/d5db0980ab004b3aada29afdcf798a8a95520240/hgtemplates/json/map);
2) Generate a map of backouts by walking the list of commits and parsing the commit message (e.g. https://hg.mozilla.org/mozilla-central/rev/2b73321e629d says "Backed out changeset 7065c7171760 ...", so you can put 7065c7171760 -> 2b73321e629d in the map); then use the map when generating the commit information.

1 would be best, but I'm not sure yet if it's feasible. CC @cgsheeh.
We will be able to do 1 when https://bugzilla.mozilla.org/show_bug.cgi?id=1523961 is fixed. So we can just wait a little.
Thanks! Will have a look on it later then. :)
I've added you to the CC list of the bug on Bugzilla, so you get notified when it is fixed.
https://bugzilla.mozilla.org/show_bug.cgi?id=1523961 has been fixed, so this is now feasible.

Resolving patch:
diff --git a/bugbug/bug_features.py b/bugbug/bug_features.py
--- a/bugbug/bug_features.py
+++ b/bugbug/bug_features.py
@@ -230,6 +230,11 @@ def __call__(self, bug, **kwargs):
         return sum(res) / len(res)
 
 
+class commit_no_of_backouts(object):
+    def __call__(self, bug, **kwargs):
+        return sum(1 for commit in bug['commits'] if commit['ever_backedout'])
+
+
 def cleanup_url(text):
     text = re.sub(r'http[s]?://(hg.mozilla|searchfox|dxr.mozilla)\S+', '__CODE_REFERENCE_URL__', text)
     return re.sub(r'http\S+', '__URL__', text)
diff --git a/bugbug/repository.py b/bugbug/repository.py
--- a/bugbug/repository.py
+++ b/bugbug/repository.py
@@ -19,7 +19,7 @@
 COMMITS_DB = 'data/commits.json'
 db.register(COMMITS_DB, 'https://www.dropbox.com/s/mz3afgncx0siijc/commits.json.xz?dl=1')
 
-Commit = namedtuple('Commit', ['node', 'author', 'desc', 'date', 'bug'])
+Commit = namedtuple('Commit', ['node', 'author', 'desc', 'date', 'bug', 'ever_backedout'])
 
 author_experience = {}
 
@@ -41,6 +41,7 @@ def _transform(commit):
         'desc': desc,
         'date': str(commit.date),
         'bug_id': commit.bug.decode('utf-8'),
+        'ever_backedout': commit.ever_backedout,
         'added': 0,
         'deleted': 0,
         'files_modified_num': 0,
@@ -82,7 +83,7 @@ def _transform(commit):
 def hg_log(repo_dir):
     hg = hglib.open(repo_dir)
 
-    template = '{node}\\0{author}\\0{desc}\\0{date}\\0{bug}\\0'
+    template = '{node}\\0{author}\\0{desc}\\0{date}\\0{bug}\\0{backedoutby}\\0'
 
     args = hglib.util.cmdbuilder(b'log', template=template, no_merges=True)
     x = hg.rawcommand(args)
@@ -99,6 +100,7 @@ def hg_log(repo_dir):
             desc=rev[2],
             date=dt,
             bug=rev[4],
+            ever_backedout=(rev[5] != b''),
         ))
 
     hg.close()


Test patch: