<p>Neels Hofmeyr <strong>merged</strong> this change.</p><p><a href="https://gerrit.osmocom.org/11706">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  osmith: Verified
  Neels Hofmeyr: Looks good to me, approved

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">gits: use git plumbing commands<br><br>Instead of 'git status' and 'git branch', which change their output<br>depending on the git version and locale, use the low-level plumbing<br>commands.<br><br>'gits status' output is exactly the same, 'gits rebase' output is<br>a bit less redundant now (that was easier to implement).<br><br>Change-Id: I42544313d14db126c99e2d9a02b8f63031944947<br>---<br>M src/gits<br>1 file changed, 92 insertions(+), 92 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/src/gits b/src/gits</span><br><span>index 81083f1..8b75278 100755</span><br><span>--- a/src/gits</span><br><span>+++ b/src/gits</span><br><span>@@ -18,7 +18,6 @@</span><br><span> </span><br><span> import sys</span><br><span> import subprocess</span><br><span style="color: hsl(0, 100%, 40%);">-import re</span><br><span> import argparse</span><br><span> import os</span><br><span> import shlex</span><br><span>@@ -28,11 +27,6 @@</span><br><span> helps to save your time with: status, fetch, rebase, ...</span><br><span> '''</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-re_status_mods = re.compile('^\t(modified|deleted):.*')</span><br><span style="color: hsl(0, 100%, 40%);">-re_status_branch_name = re.compile('On branch ([^ ]*)')</span><br><span style="color: hsl(0, 100%, 40%);">-re_branch_name = re.compile('^..([^ ]+) .*')</span><br><span style="color: hsl(0, 100%, 40%);">-re_ahead_behind = re.compile('ahead [0-9]+|behind [0-9]+')</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> </span><br><span> def error(*msgs):</span><br><span>     sys.stderr.write(''.join(msgs))</span><br><span>@@ -67,52 +61,69 @@</span><br><span>     return subprocess.check_output(['git', '-C', git_dir, ] + list(args)).decode('utf-8')</span><br><span> </span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-def git_branch(git_dir):</span><br><span style="color: hsl(0, 100%, 40%);">-    status = git_output(git_dir, 'status', '--long')</span><br><span style="color: hsl(0, 100%, 40%);">-    m = re_status_branch_name.find(status)</span><br><span style="color: hsl(0, 100%, 40%);">-    if not m:</span><br><span style="color: hsl(0, 100%, 40%);">-        error('No current branch in %r' % git_dir)</span><br><span style="color: hsl(0, 100%, 40%);">-    return m.group(1)</span><br><span style="color: hsl(120, 100%, 40%);">+def git_bool(git_dir, *args):</span><br><span style="color: hsl(120, 100%, 40%);">+    try:</span><br><span style="color: hsl(120, 100%, 40%);">+        subprocess.check_output(['git', '-C', git_dir, ] + list(args))</span><br><span style="color: hsl(120, 100%, 40%);">+        return True</span><br><span style="color: hsl(120, 100%, 40%);">+    except subprocess.CalledProcessError as e:</span><br><span style="color: hsl(120, 100%, 40%);">+        return False</span><br><span> </span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-def git_status(git_dir, verbose=False):</span><br><span style="color: hsl(0, 100%, 40%);">-    status_lines = git_output(git_dir, 'status').splitlines()</span><br><span style="color: hsl(0, 100%, 40%);">-    if verbose and len(status_lines):</span><br><span style="color: hsl(0, 100%, 40%);">-        print(status_lines[0])</span><br><span style="color: hsl(120, 100%, 40%);">+def git_branch_exists(git_dir, branch='origin/master'):</span><br><span style="color: hsl(120, 100%, 40%);">+    return git_bool(git_dir, 'rev-parse', '--quiet', '--verify', branch)</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    on_branch = None</span><br><span style="color: hsl(0, 100%, 40%);">-    branch_status_str = None</span><br><span style="color: hsl(0, 100%, 40%);">-    local_mods = False</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    ON_BRANCH = 'On branch '</span><br><span style="color: hsl(0, 100%, 40%);">-    STATUS = 'Your branch'</span><br><span style="color: hsl(120, 100%, 40%);">+def git_ahead_behind(git_dir, branch='master', remote='origin'):</span><br><span style="color: hsl(120, 100%, 40%);">+    ''' Count revisions ahead/behind of the remote branch.</span><br><span style="color: hsl(120, 100%, 40%);">+        returns: (ahead, behind) (e.g. (0, 5)) '''</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    for l in status_lines:</span><br><span style="color: hsl(0, 100%, 40%);">-        if l.startswith(ON_BRANCH):</span><br><span style="color: hsl(0, 100%, 40%);">-            if on_branch:</span><br><span style="color: hsl(0, 100%, 40%);">-                error('cannot parse status, more than one branch?')</span><br><span style="color: hsl(0, 100%, 40%);">-            on_branch = l[len(ON_BRANCH):]</span><br><span style="color: hsl(0, 100%, 40%);">-        elif l.startswith(STATUS):</span><br><span style="color: hsl(0, 100%, 40%);">-            if 'Your branch is up to date' in l:</span><br><span style="color: hsl(0, 100%, 40%);">-                branch_status_str = l</span><br><span style="color: hsl(0, 100%, 40%);">-            elif 'Your branch is up-to-date' in l:</span><br><span style="color: hsl(0, 100%, 40%);">-                branch_status_str = l</span><br><span style="color: hsl(0, 100%, 40%);">-            elif 'Your branch is ahead' in l:</span><br><span style="color: hsl(0, 100%, 40%);">-                branch_status_str = 'ahead: ' + l</span><br><span style="color: hsl(0, 100%, 40%);">-            elif 'Your branch is behind' in l:</span><br><span style="color: hsl(0, 100%, 40%);">-                branch_status_str = 'behind: ' + l</span><br><span style="color: hsl(0, 100%, 40%);">-            elif 'have diverged' in l:</span><br><span style="color: hsl(0, 100%, 40%);">-                branch_status_str = 'diverged: ' + l</span><br><span style="color: hsl(0, 100%, 40%);">-            else:</span><br><span style="color: hsl(0, 100%, 40%);">-                error('unknown status str: %r' % l)</span><br><span style="color: hsl(0, 100%, 40%);">-        else:</span><br><span style="color: hsl(0, 100%, 40%);">-            m = re_status_mods.match(l)</span><br><span style="color: hsl(0, 100%, 40%);">-            if m:</span><br><span style="color: hsl(0, 100%, 40%);">-                local_mods = True</span><br><span style="color: hsl(120, 100%, 40%);">+    # Missing remote branch</span><br><span style="color: hsl(120, 100%, 40%);">+    if not git_branch_exists(git_dir, remote + '/' + branch):</span><br><span style="color: hsl(120, 100%, 40%);">+        return (0, 0)</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    if verbose:</span><br><span style="color: hsl(0, 100%, 40%);">-        print('%s%s' % (branch_status_str, '\nLOCAL MODS' if local_mods else ''))</span><br><span style="color: hsl(0, 100%, 40%);">-    return (on_branch, branch_status_str, local_mods)</span><br><span style="color: hsl(120, 100%, 40%);">+    behind = git_output(git_dir, 'rev-list', '--count', '%s..%s/%s' % (branch, remote, branch))</span><br><span style="color: hsl(120, 100%, 40%);">+    ahead = git_output(git_dir, 'rev-list', '--count', '%s/%s..%s' % (remote, branch, branch))</span><br><span style="color: hsl(120, 100%, 40%);">+    return (int(ahead.rstrip()), int(behind.rstrip()))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+def git_branches(git_dir, obj='refs/heads'):</span><br><span style="color: hsl(120, 100%, 40%);">+    ret = git_output(git_dir, 'for-each-ref', obj, '--format', '%(refname:short)')</span><br><span style="color: hsl(120, 100%, 40%);">+    return ret.splitlines()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+def git_branch_current(git_dir):</span><br><span style="color: hsl(120, 100%, 40%);">+    ret = git_output(git_dir, 'rev-parse', '--abbrev-ref', 'HEAD').rstrip()</span><br><span style="color: hsl(120, 100%, 40%);">+    if ret == 'HEAD':</span><br><span style="color: hsl(120, 100%, 40%);">+        return None</span><br><span style="color: hsl(120, 100%, 40%);">+    return ret</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+def git_has_modifications(git_dir):</span><br><span style="color: hsl(120, 100%, 40%);">+    return not git_bool(git_dir, 'diff-index', '--quiet', 'HEAD')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+def git_can_fast_forward(git_dir, branch='master', remote='origin'):</span><br><span style="color: hsl(120, 100%, 40%);">+    return git_bool(git_dir, 'merge-base', '--is-ancestor', 'HEAD', remote + '/' + branch)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+def format_branch_ahead_behind(branch, ahead, behind):</span><br><span style="color: hsl(120, 100%, 40%);">+    ''' branch: string like "master"</span><br><span style="color: hsl(120, 100%, 40%);">+        ahead, behind: integers like 5, 3</span><br><span style="color: hsl(120, 100%, 40%);">+        returns: string like "master", "master[+5]", "master[-3]", "master[+5|-3]" '''</span><br><span style="color: hsl(120, 100%, 40%);">+    # Just the branch</span><br><span style="color: hsl(120, 100%, 40%);">+    if not ahead and not behind:</span><br><span style="color: hsl(120, 100%, 40%);">+        return branch</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    # Suffix with ahead/behind</span><br><span style="color: hsl(120, 100%, 40%);">+    ret = branch + '['</span><br><span style="color: hsl(120, 100%, 40%);">+    if ahead:</span><br><span style="color: hsl(120, 100%, 40%);">+        ret += '+' + str(ahead)</span><br><span style="color: hsl(120, 100%, 40%);">+        if behind:</span><br><span style="color: hsl(120, 100%, 40%);">+            ret += '|'</span><br><span style="color: hsl(120, 100%, 40%);">+    if behind:</span><br><span style="color: hsl(120, 100%, 40%);">+        ret += '-' + str(behind)</span><br><span style="color: hsl(120, 100%, 40%);">+    ret += ']'</span><br><span style="color: hsl(120, 100%, 40%);">+    return ret</span><br><span> </span><br><span> </span><br><span> def git_branch_summary(git_dir):</span><br><span>@@ -122,36 +133,22 @@</span><br><span>     interesting_branch_names = ('master',)</span><br><span> </span><br><span>     strs = [git_dir, ]</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    on_branch, branch_status_str, has_mods = git_status(git_dir)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    if has_mods:</span><br><span style="color: hsl(120, 100%, 40%);">+    if git_has_modifications(git_dir):</span><br><span>         strs.append('MODS')</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    branch_strs = git_output(git_dir, 'branch', '-vv').splitlines()</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    for line in branch_strs:</span><br><span style="color: hsl(0, 100%, 40%);">-        m = re_branch_name.match(line)</span><br><span style="color: hsl(0, 100%, 40%);">-        name = m.group(1)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        current_branch = False</span><br><span style="color: hsl(0, 100%, 40%);">-        if line.startswith('*'):</span><br><span style="color: hsl(0, 100%, 40%);">-            current_branch = True</span><br><span style="color: hsl(0, 100%, 40%);">-        elif name not in interesting_branch_names:</span><br><span style="color: hsl(120, 100%, 40%);">+    branch_current = git_branch_current(git_dir)</span><br><span style="color: hsl(120, 100%, 40%);">+    for branch in git_branches(git_dir):</span><br><span style="color: hsl(120, 100%, 40%);">+        is_current = (branch == branch_current)</span><br><span style="color: hsl(120, 100%, 40%);">+        if not is_current and branch not in interesting_branch_names:</span><br><span>             continue</span><br><span style="color: hsl(0, 100%, 40%);">-        ahead_behind = re_ahead_behind.findall(line)</span><br><span style="color: hsl(0, 100%, 40%);">-        if not ahead_behind and not current_branch:</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        ahead, behind = git_ahead_behind(git_dir, branch)</span><br><span style="color: hsl(120, 100%, 40%);">+        if not ahead and not behind and not is_current:</span><br><span>             # skip branches that are "not interesting"</span><br><span>             continue</span><br><span style="color: hsl(0, 100%, 40%);">-        ahead_behind = [</span><br><span style="color: hsl(0, 100%, 40%);">-            x.replace('ahead ', '+').replace('behind ', '-') for x in ahead_behind]</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-        branch_info = name</span><br><span style="color: hsl(0, 100%, 40%);">-        if ahead_behind:</span><br><span style="color: hsl(0, 100%, 40%);">-            branch_info = branch_info + ('[%s]' % '|'.join(ahead_behind))</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        strs.append(''.join(branch_info))</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(120, 100%, 40%);">+        # Branch with ahead/behind origin info ("master[+1|-5]")</span><br><span style="color: hsl(120, 100%, 40%);">+        strs.append(format_branch_ahead_behind(branch, ahead, behind))</span><br><span>     return strs</span><br><span> </span><br><span> </span><br><span>@@ -236,14 +233,15 @@</span><br><span> </span><br><span> </span><br><span> def rebase(git_dir):</span><br><span style="color: hsl(0, 100%, 40%);">-    orig_branch, branch_status_str, local_mods = git_status(</span><br><span style="color: hsl(0, 100%, 40%);">-        git_dir, verbose=True)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(120, 100%, 40%);">+    orig_branch = git_branch_current(git_dir)</span><br><span>     if orig_branch is None:</span><br><span>         print('Not on a branch: %s' % git_dir)</span><br><span>         raise SkipThisRepo()</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    if local_mods:</span><br><span style="color: hsl(120, 100%, 40%);">+    print('Rebasing branch: ' + orig_branch)</span><br><span style="color: hsl(120, 100%, 40%);">+    ahead, behind = git_ahead_behind(git_dir, orig_branch)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if git_has_modifications(git_dir):</span><br><span>         do_commit = ask(git_dir, 'Local mods.',</span><br><span>                         'c  commit to this branch',</span><br><span>                         '<name>  commit to new branch',</span><br><span>@@ -259,17 +257,29 @@</span><br><span>             git(git_dir, 'commit', '-am', 'wip', may_fail=True)</span><br><span>             git(git_dir, 'checkout', orig_branch)</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-        _, _, local_mods = git_status(git_dir)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        if local_mods:</span><br><span style="color: hsl(120, 100%, 40%);">+        if git_has_modifications(git_dir):</span><br><span>             print('There still are local modifications')</span><br><span>             raise SkipThisRepo()</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    if branch_status_str is None:</span><br><span style="color: hsl(120, 100%, 40%);">+    # Missing upstream branch</span><br><span style="color: hsl(120, 100%, 40%);">+    if not git_branch_exists(git_dir, 'origin/' + orig_branch):</span><br><span>         print('there is no upstream branch for %r' % orig_branch)</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    elif branch_status_str.startswith('behind'):</span><br><span style="color: hsl(0, 100%, 40%);">-        if 'and can be fast-forwarded' in branch_status_str:</span><br><span style="color: hsl(120, 100%, 40%);">+    # Diverged</span><br><span style="color: hsl(120, 100%, 40%);">+    elif ahead and behind:</span><br><span style="color: hsl(120, 100%, 40%);">+        do_reset = ask(git_dir, 'Diverged.',</span><br><span style="color: hsl(120, 100%, 40%);">+                       '%s: git reset --hard origin/%s?' % (</span><br><span style="color: hsl(120, 100%, 40%);">+                           orig_branch, orig_branch),</span><br><span style="color: hsl(120, 100%, 40%);">+                       '<empty>  no',</span><br><span style="color: hsl(120, 100%, 40%);">+                       'OK  yes (write OK in caps!)',</span><br><span style="color: hsl(120, 100%, 40%);">+                       valid_answers=('', 'OK'))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if do_reset == 'OK':</span><br><span style="color: hsl(120, 100%, 40%);">+            git(git_dir, 'reset', '--hard', 'origin/%s' % orig_branch)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    # Behind</span><br><span style="color: hsl(120, 100%, 40%);">+    elif behind:</span><br><span style="color: hsl(120, 100%, 40%);">+        if git_can_fast_forward(git_dir, orig_branch):</span><br><span>             print('fast-forwarding...')</span><br><span>             git(git_dir, 'merge')</span><br><span>         else:</span><br><span>@@ -282,7 +292,8 @@</span><br><span>             if do_merge == 'ok':</span><br><span>                 git(git_dir, 'merge')</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    elif branch_status_str.startswith('ahead'):</span><br><span style="color: hsl(120, 100%, 40%);">+    # Ahead</span><br><span style="color: hsl(120, 100%, 40%);">+    elif ahead:</span><br><span>         do_commit = ask(git_dir, 'Ahead. commit to new branch?',</span><br><span>                         '<empty>  no',</span><br><span>                         '<name>   create new branch',</span><br><span>@@ -300,17 +311,6 @@</span><br><span>         if do_reset == 'OK':</span><br><span>             git(git_dir, 'reset', '--hard', 'origin/%s' % orig_branch)</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    elif branch_status_str.startswith('diverged'):</span><br><span style="color: hsl(0, 100%, 40%);">-        do_reset = ask(git_dir, 'Diverged.',</span><br><span style="color: hsl(0, 100%, 40%);">-                       '%s: git reset --hard origin/%s?' % (</span><br><span style="color: hsl(0, 100%, 40%);">-                           orig_branch, orig_branch),</span><br><span style="color: hsl(0, 100%, 40%);">-                       '<empty>  no',</span><br><span style="color: hsl(0, 100%, 40%);">-                       'OK  yes (write OK in caps!)',</span><br><span style="color: hsl(0, 100%, 40%);">-                       valid_answers=('', 'OK'))</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        if do_reset == 'OK':</span><br><span style="color: hsl(0, 100%, 40%);">-            git(git_dir, 'reset', '--hard', 'origin/%s' % orig_branch)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>     return orig_branch</span><br><span> </span><br><span> </span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/11706">change 11706</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/11706"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: osmo-dev </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-MessageType: merged </div>
<div style="display:none"> Gerrit-Change-Id: I42544313d14db126c99e2d9a02b8f63031944947 </div>
<div style="display:none"> Gerrit-Change-Number: 11706 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: osmith <osmith@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Neels Hofmeyr <nhofmeyr@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: osmith <osmith@sysmocom.de> </div>