diff --git a/git-prompt.c b/git-prompt.c
new file mode 100644
index 0000000..db158b7
--- /dev/null
+++ b/git-prompt.c
@@ -0,0 +1,200 @@
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#if __linux__
+#include <sys/wait.h>
+#endif
+
+#define check(CONDITION)                                                    \
+  if (CONDITION) {                                                          \
+    fprintf(stderr, "error: %s: %d: %s\n", __FILE__, __LINE__, #CONDITION); \
+    exit(0);                                                                \
+  }
+
+typedef struct process {
+  pid_t pid;
+  FILE* out;
+} process_t;
+
+process_t process_open(char* command) {
+  int fds[2];
+  check(pipe(fds));
+  int pid = fork();
+  check(pid == -1);
+  if (pid == 0) { // child process
+    close(fds[0]);
+    dup2(fds[1], STDOUT_FILENO);
+    dup2(STDOUT_FILENO, STDERR_FILENO);
+    char* argv[] = {"sh", "-c", command, NULL};
+    exit(execvp(argv[0], argv));
+  } else { // parent process
+    close(fds[1]);
+    process_t process = {pid, fdopen(fds[0], "rb")};
+    return process;
+  }
+}
+
+int process_close(process_t process) {
+  fclose(process.out);
+  int status;
+  check(process.pid != waitpid(process.pid, &status, 0));
+  printf("status: %d\nWIFEXITED: %d\nWEXITSTATUS: %d\n", status,
+         WIFEXITED(status), WEXITSTATUS(status));
+  if (WIFEXITED(status)) {
+    return WEXITSTATUS(status);
+  }
+  return 0;
+}
+
+char* trim(char* str) {
+  char* end;
+  while (isspace((unsigned char)*str)) str++;
+  if (*str == 0) {
+    return str;
+  }
+  end = str + strlen(str) - 1;
+  while (end > str && isspace((unsigned char)*end)) {
+    end--;
+  }
+  end[1] = '\0';
+  return str;
+}
+
+char* append(char* buffer, int count, ...) {
+  va_list list;
+  int i;
+  va_start(list, count);
+  for (i = 0; i < count; i++) {
+    strcat(buffer, va_arg(list, char*));
+  }
+  va_end(list);
+  return buffer;
+}
+
+char* inttostr(char* buffer, int value) {
+  sprintf(buffer, "%d", value);
+  return buffer;
+}
+
+int main() {
+  process_t process;
+  // check we are in a git repo
+  process = process_open("git rev-parse --git-dir");
+  check(process_close(process));
+
+  // get the current branch name
+  process = process_open("git symbolic-ref --short HEAD");
+  char branch_buf[256] = {};
+  fread(branch_buf, 1, sizeof(branch_buf), process.out);
+  if (process_close(process)) {
+    // current HEAD is not a symbolic ref
+    process = process_open("git rev-parse --abbrev-ref HEAD");
+    memset(branch_buf, 0, sizeof(branch_buf));
+    fread(branch_buf, 1, sizeof(branch_buf), process.out);
+    check(process_close(process));
+    if (strcmp("HEAD", trim(branch_buf)) == 0) {
+      // get the commit hash
+      process = process_open("git rev-parse --short HEAD");
+      memset(branch_buf, 0, sizeof(branch_buf));
+      fread(branch_buf, 1, sizeof(branch_buf), process.out);
+      check(process_close(process));
+    }
+  }
+  char* branch = trim(branch_buf);
+  char prompt[1024] = {};
+  append(prompt, 3, " %{%F{66}%}", branch, "%{%f%}");
+
+  // get the upstream remote if one exists
+  char command[1024] = {};
+  append(command, 3, "git config branch.", branch, ".remote");
+  process = process_open(command);
+  char remote_buf[256] = {};
+  fread(remote_buf, 1, sizeof(remote_buf), process.out);
+  if (process_close(process) == 0) {
+    char* remote = trim(remote_buf);
+    // get the number of commits ahead of the remote
+    memset(command, 0, sizeof(command));
+    process = process_open(append(command, 5,
+                                  "git rev-list --right-only refs/remotes/",
+                                  remote, "/", branch, "...HEAD --count"));
+    char count[32] = {};
+    fread(count, 1, sizeof(count), process.out);
+    if(process_close(process) == 0 && strcmp("0", trim(count))) {
+      append(prompt, 2, "↑", trim(count));
+    }
+
+    // get the number of commits behind the remote
+    memset(command, 0, sizeof(command));
+    process = process_open(append(command, 5,
+                                  "git rev-list --left-only refs/remotes/",
+                                  remote, "/", branch, "...HEAD --count"));
+    memset(count, 0, sizeof(count));
+    fread(count, 1, sizeof(count), process.out);
+    if(process_close(process) == 0 && strcmp("0", trim(count))) {
+      append(prompt, 2, "↓", trim(count));
+    }
+  }
+  append(prompt, 1, " ");
+
+  // get the status and parse it
+  process = process_open("git status --porcelain");
+  char status[2048];
+  int indexed = 0, modified = 0, deleted = 0, untracked = 0, unmerged = 0;
+  while (NULL != fgets(status, sizeof(status) - 1, process.out)) {
+    char X = status[0];
+    char Y = status[1];
+    if (X == '?' && Y == '?') {
+      ++untracked;
+    } else if ((X == 'A' && (Y == 'A' || Y == 'U')) ||
+               (X == 'D' && (Y == 'D' || Y == 'U')) ||
+               (X == 'U' && (Y == 'A' || Y == 'D' || Y == 'D' || Y == 'U'))) {
+      ++unmerged;
+    } else {
+      switch (X) {
+      case ' ':
+        switch (Y) {
+          case 'M': ++modified; break;
+          case 'D': ++deleted; break;
+        } break;
+      case 'D': ++indexed;
+        switch (Y) {
+          case ' ': break;
+          case 'M': ++modified; break;
+        } break;
+      case 'M': case 'A': case 'R': case 'C': ++indexed;
+        switch (Y) {
+          case ' ': break;
+          case 'M': ++modified; break;
+          case 'D': ++deleted; break;
+        } break;
+      }
+    }
+  }
+  check(process_close(process));
+  if (indexed || modified || deleted || unmerged || untracked) {  // modified
+    char int_buf[32];
+    if (indexed) {
+      append(prompt, 3, "%{%F{2}%}*", inttostr(int_buf, indexed), "%{%f%}");
+    }
+    if (modified) {
+      append(prompt, 3, "%{%F{1}%}+", inttostr(int_buf, modified), "%{%f%}");
+    }
+    if (deleted) {
+      append(prompt, 3, "%{%F{1}%}-", inttostr(int_buf, deleted), "%{%f%}");
+    }
+    if (unmerged) {
+      append(prompt, 3, "%{%B%F{1}%}×", inttostr(int_buf, unmerged), "%{%f%}");
+    }
+    if (untracked) {
+      append(prompt, 1, "%{%F{1}%}…%{%f%}");
+    }
+  } else {  // clean
+    append(prompt, 1, "%{%B%F{2}%}✓%{%f%b%}");
+  }
+  // print the prompt
+  puts(prompt);
+  return 0;
+}
diff --git a/prompt_fresh_setup b/prompt_fresh_setup
index 7bc4666..ecb26f2 100644
--- a/prompt_fresh_setup
+++ b/prompt_fresh_setup
@@ -1,17 +1,18 @@
 prompt_fresh_help() {
   echo "\
 options:
-        -a      enable almostontop like behaviour"
+        -a      enable almostontop like behaviour
+        -r      recompile git-prompt executable"
 }
 
 prompt_fresh_setup() {
-  fresh_compile_git_prompt
-
   # Parse options
   local almostontop=0
-  while getopts 'a' opt; do
+  local recompile=0
+  while getopts 'ar' opt; do
     case $opt in
       a) local almostontop=1 ;;
+      r) local recompile=1 ;;
       *) prompt -h fresh; return 1 ;;
     esac
   done
@@ -31,6 +32,11 @@ prompt_fresh_setup() {
     add-zsh-hook -d preexec fresh_almostontop
   fi
 
+  if [ $recompile -eq 1 ]; then
+    [ -f ~/.cache/zsh/git-prompt ] && rm ~/.cache/zsh/git-prompt
+  fi
+  fresh_compile_git_prompt
+
   if [ "$USER" = "root" ]; then
     local user="%{%F{1}%}%n%{%f%}"
   else
@@ -50,7 +56,7 @@ prompt_fresh_setup() {
 }
 
 prompt_cleanup() {
-  if [ -f ~/.cache/zsh/git-prompt ]; then rm ~/.cache/zsh/git-prompt; fi
+  [ -f ~/.cache/zsh/git-prompt ] && rm ~/.cache/zsh/git-prompt
 }
 
 fresh_line_one() {
@@ -64,58 +70,7 @@ fresh_line_one() {
     local directory="%{%F{37}%}%~%{%f%}"
 
   # Check we are in a git repository
-  if `git rev-parse --git-dir > /dev/null 2>&1`; then
-    # Get git branch name & exit early if not found
-    local branch="`git symbolic-ref HEAD 2> /dev/null`"
-    if [ "" = "$branch" ]; then
-      local branch="`git rev-parse --abbrev-ref HEAD`"
-      if [ "HEAD" = "$branch" ]; then
-        local branch="`git rev-parse --short $branch`"
-      fi
-    fi
-
-    if [ "$branch" != "" ]; then
-      local branch=${branch#refs/heads/}
-      # Get the number of commits ahead/behind from the remote branch
-      local remote="`git config branch.$branch.remote 2> /dev/null`"
-      if [ "" != "remote" ]; then
-        local branches="refs/remotes/$remote/$branch...HEAD"
-        local nahead=`git rev-list --right-only $branches --count 2> /dev/null`
-        if [ "0" -ne "$nahead" ]; then local ahead="↑$nahead"; fi
-        local nbehind=`git rev-list --left-only $branches --count 2> /dev/null`
-        if [ "0" -ne "$nbehind" ]; then local behind="↓$nbehind"; fi
-      fi
-
-      # Construct the git prompt
-      local git=" %{%F{66}%}$branch%{%f%}$ahead$behind "
-
-      if [ -f ~/.cache/zsh/git-prompt ]; then
-        # Get the change counts from git-prompt
-        local counts=(`~/.cache/zsh/git-prompt`)
-
-        # Parse the results from git-prompt
-        if [ 0 -eq ${#counts[@]} ]; then # clean
-          local git="$git%{%B%F{2}%}✓%{%f%b%}";
-        else
-          if [ 0 != $counts[1] ]; then  # indexed
-            local git="$git%{%F{2}%}*$counts[1]%{%f%}"
-          fi
-          if [ 0 != $counts[2] ]; then  # modified
-            local git="$git%{%F{1}%}+$counts[2]%{%f%}";
-          fi
-          if [ 0 != $counts[3] ]; then  # deleted
-            local git="$git%{%F{1}%}-$counts[3]%{%f%}";
-          fi
-          if [ 0 != $counts[4] ]; then  # unmerged
-            local git="$git%{%B%F{1}%}×$counts[4]%{%f%b%}";
-          fi
-          if [ 0 != $counts[5] ]; then  # untracked
-            local git="$git%{%F{1}%}…%{%f%}";
-          fi
-        fi
-      fi
-    fi
-  fi
+  local git=`~/.cache/zsh/git-prompt`
 
   # If the last command failed, display its error code at the right
   if [[ $exit_code -ne 0 ]]; then
@@ -148,20 +103,145 @@ fresh_compile_git_prompt() {
   local cache=~/.cache/zsh; [ ! -d $cache ] && mkdir -p $cache
   if [ ! -f $cache/git-prompt ]; then
     cc -x c -std=gnu99 -O3 -DNDEBUG -o $cache/git-prompt - 2> /dev/null << EOF
+#include <ctype.h>
+#include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define check(CONDITION) if (CONDITION) { exit(0); }
+
+typedef struct process {
+  pid_t pid;
+  FILE* out;
+} process_t;
+
+process_t process_open(char* command) {
+  int fds[2];
+  check(pipe(fds));
+  int pid = fork();
+  check(pid == -1);
+  if (pid == 0) { // child process
+    close(fds[0]);
+    dup2(fds[1], STDOUT_FILENO);
+    dup2(STDOUT_FILENO, STDERR_FILENO);
+    char* argv[] = {"sh", "-c", command, NULL};
+    exit(execvp(argv[0], argv));
+  } else { // parent process
+    close(fds[1]);
+    process_t process = {pid, fdopen(fds[0], "rb")};
+    return process;
+  }
+}
+
+int process_close(process_t process) {
+  fclose(process.out);
+  int status;
+  check(process.pid != waitpid(process.pid, &status, 0));
+  if (WIFEXITED(status)) {
+    return WEXITSTATUS(status);
+  }
+  return 0;
+}
+
+const char* trim(char* str) {
+  char* end;
+  while (isspace((unsigned char)*str)) str++;
+  if (*str == 0) {
+    return str;
+  }
+  end = str + strlen(str) - 1;
+  while (end > str && isspace((unsigned char)*end)) {
+    end--;
+  }
+  end[1] = '\0';
+  return str;
+}
+
+const char* append(char* buffer, int count, ...) {
+  va_list list;
+  int i;
+  va_start(list, count);
+  for (i = 0; i < count; i++) {
+    strcat(buffer, va_arg(list, const char*));
+  }
+  va_end(list);
+  return buffer;
+}
+
+const char* inttostr(char* buffer, int value) {
+  sprintf(buffer, "%d", value);
+  return buffer;
+}
 
 int main() {
-  FILE *file = popen("git status --porcelain", "r");
-  if (NULL == file) { return 0; }
+  process_t process;
+  // check we are in a git repo
+  process = process_open("git rev-parse --git-dir");
+  check(process_close(process));
 
+  // get the current branch name
+  process = process_open("git symbolic-ref --short HEAD");
+  char branch_buf[256] = {};
+  fread(branch_buf, 1, sizeof(branch_buf), process.out);
+  if (process_close(process)) {
+    // current HEAD is not a symbolic ref
+    process = process_open("git rev-parse --abbrev-ref HEAD");
+    memset(branch_buf, 0, sizeof(branch_buf));
+    fread(branch_buf, 1, sizeof(branch_buf), process.out);
+    check(process_close(process));
+    if (strcmp("HEAD", trim(branch_buf)) == 0) {
+      // get the commit hash
+      process = process_open("git rev-parse --short HEAD");
+      memset(branch_buf, 0, sizeof(branch_buf));
+      fread(branch_buf, 1, sizeof(branch_buf), process.out);
+      check(process_close(process));
+    }
+  }
+  const char* branch = trim(branch_buf);
+  char prompt[1024] = {};
+  append(prompt, 3, " %{%F{66}%}", branch, "%{%f%}");
+
+  // get the upstream remote if one exists
+  char command[1024] = {};
+  append(command, 3, "git config branch.", branch, ".remote");
+  process = process_open(command);
+  char remote_buf[256] = {};
+  fread(remote_buf, 1, sizeof(remote_buf), process.out);
+  if (process_close(process) == 0) {
+    const char* remote = trim(remote_buf);
+    // get the number of commits ahead of the remote
+    memset(command, 0, sizeof(command));
+    process = process_open(append(command, 5,
+                                  "git rev-list --right-only refs/remotes/",
+                                  remote, "/", branch, "...HEAD --count"));
+    char count[32] = {};
+    fread(count, 1, sizeof(count), process.out);
+    if(process_close(process) == 0 && strcmp("0", trim(count))) {
+      append(prompt, 2, "↑", trim(count));
+    }
+
+    // get the number of commits behind the remote
+    memset(command, 0, sizeof(command));
+    process = process_open(append(command, 5,
+                                  "git rev-list --left-only refs/remotes/",
+                                  remote, "/", branch, "...HEAD --count"));
+    memset(count, 0, sizeof(count));
+    fread(count, 1, sizeof(count), process.out);
+    if(process_close(process) == 0 && strcmp("0", trim(count))) {
+      append(prompt, 2, "↓", trim(count));
+    }
+  }
+  append(prompt, 1, " ");
+
+  // get the status and parse it
+  process = process_open("git status --porcelain");
   char status[2048];
   int indexed = 0, modified = 0, deleted = 0, untracked = 0, unmerged = 0;
-
-  while (NULL != fgets(status, sizeof(status) - 1, file)) {
+  while (NULL != fgets(status, sizeof(status) - 1, process.out)) {
     char X = status[0];
     char Y = status[1];
-
     if (X == '?' && Y == '?') {
       ++untracked;
     } else if ((X == 'A' && (Y == 'A' || Y == 'U')) ||
@@ -189,18 +269,34 @@ int main() {
       }
     }
   }
-
-  pclose(file);
-
-  if (indexed || modified || deleted || unmerged || untracked) {
-    printf("%d %d %d %d %d", indexed, modified, deleted, unmerged, untracked);
+  check(process_close(process));
+  if (indexed || modified || deleted || unmerged || untracked) {  // modified
+    char int_buf[32];
+    if (indexed) {
+      append(prompt, 3, "%{%F{2}%}*", inttostr(int_buf, indexed), "%{%f%}");
+    }
+    if (modified) {
+      append(prompt, 3, "%{%F{1}%}+", inttostr(int_buf, modified), "%{%f%}");
+    }
+    if (deleted) {
+      append(prompt, 3, "%{%F{1}%}-", inttostr(int_buf, deleted), "%{%f%}");
+    }
+    if (unmerged) {
+      append(prompt, 3, "%{%B%F{1}%}×", inttostr(int_buf, unmerged), "%{%f%}");
+    }
+    if (untracked) {
+      append(prompt, 1, "%{%F{1}%}…%{%f%}");
+    }
+  } else {  // clean
+    append(prompt, 1, "%{%B%F{2}%}✓%{%f%b%}");
   }
-
+  // print the prompt
+  puts(prompt);
   return 0;
 }
 EOF
     if [ $? -ne 0 ]; then
-      echo "git prompt disabled, are the system development headers installed?"
+      echo "git prompt not compiled, are the system development headers installed?"
     fi
   fi
 }