/* * Get the name of the current git branch (pointed to by HEAD) and print it * out, color-coded to show if any working files are dirty. This would * ideally be implemented as a shell script, but I wrote it in C because it * needs to be fast! And we all know C is fast, amiright? * * Also reads in regular expressions (one per line) matching paths that * should be ignored. Each regex is matched against the top-level * directory path of the repository. If it matches, nothing is printed. * It is a good idea to ignore repositories on slow (remote) disks. * * Example usage (bash): * PS1='\u@\h \w$(githead <~/.nogitprompt) $ ' * * Written 04 June 2012 by Charles McGarvey. */ const char* format = "[%sm %s"; const char* ansi_codes = "0;36"; const char* ansi_codes_dirty = "0;31"; const int short_hash_length = 7; #include #include #include #include #include #include #include #include int main(int argc, char* argv[]) { char buf[4096]; char* ref = NULL; int dirty = 0; fclose(stderr); // usually do not want any extra junk printed int p[2]; if (pipe(p) == -1) { fprintf(stderr, "pipe failed: %s\n", strerror(errno)); return 1; } pid_t pid1 = fork(); if (pid1 == -1) { fprintf(stderr, "fork failed: %s\n", strerror(errno)); return 1; } if (pid1 == 0) { char* args[] = {"git", "rev-parse", "--show-toplevel", NULL}; if (dup2(p[1], 1) == -1) { fprintf(stderr, "dup2 failed: %s\n", strerror(errno)); } execvp(args[0], args); return 0; } close(p[1]); int status; while (waitpid(pid1, &status, 0) < 0) { if (errno != EINTR) { fprintf(stderr, "waitpid failed: %s\n", strerror(errno)); return 1; } } if (WEXITSTATUS(status) == 0) { ssize_t bytes = read(p[0], buf, sizeof(buf)); if (bytes < 0) { fprintf(stderr, "read failed: %s\n", strerror(errno)); return 1; } buf[bytes-1] = '\0'; char regbuf[4096]; while (gets(regbuf)) { char errbuf[4096]; regex_t re; int err = regcomp(&re, regbuf, REG_EXTENDED | REG_ICASE | REG_NOSUB); if (err != 0) { regerror(err, &re, errbuf, sizeof(errbuf)); fprintf(stderr, "regcomp failed: %s\n", errbuf); continue; } if (regexec(&re, buf, 0, NULL, 0) == 0) { return 0; } regfree(&re); } } else { fprintf(stderr, "git failed; bailing out...\n"); return 1; } pid_t pid2 = fork(); if (pid2 == -1) { fprintf(stderr, "fork failed: %s\n", strerror(errno)); return 1; } if (pid2 == 0) { char* args[] = {"git", "diff", "--no-ext-diff", "--quiet", "--exit-code", NULL}; execvp(args[0], args); return 0; } if (pipe(p) == -1) { fprintf(stderr, "pipe failed: %s\n", strerror(errno)); return 1; } pid_t pid3 = fork(); if (pid3 == -1) { fprintf(stderr, "fork failed: %s\n", strerror(errno)); return 1; } if (pid3 == 0) { char* args[] = {"git", "symbolic-ref", "HEAD", NULL}; if (dup2(p[1], 1) == -1) { fprintf(stderr, "dup2 failed: %s\n", strerror(errno)); } execvp(args[0], args); return 1; } close(p[1]); while (waitpid(pid3, &status, 0) < 0) { if (errno != EINTR) { fprintf(stderr, "waitpid failed: %s\n", strerror(errno)); return 1; } } if (WEXITSTATUS(status) == 0) { ssize_t bytes = read(p[0], buf, sizeof(buf)); if (bytes < 0) { fprintf(stderr, "read failed: %s\n", strerror(errno)); return 1; } buf[bytes-1] = '\0'; ref = buf + 11; } else { if (pipe(p) == -1) { fprintf(stderr, "pipe failed: %s\n", strerror(errno)); return 1; } pid_t pid4 = fork(); if (pid4 == -1) { fprintf(stderr, "fork failed: %s\n", strerror(errno)); return 1; } if (pid4 == 0) { char* args[] = {"git", "rev-parse", "HEAD", NULL}; if (dup2(p[1], 1) == -1) { fprintf(stderr, "dup2 failed: %s\n", strerror(errno)); } execvp(args[0], args); return 1; } close(p[1]); while (waitpid(pid4, &status, 0) < 0) { if (errno != EINTR) { fprintf(stderr, "waitpid failed: %s\n", strerror(errno)); return 1; } } if (WEXITSTATUS(status) == 0) { ssize_t bytes = read(p[0], buf, sizeof(buf)); if (bytes < 0) { fprintf(stderr, "read failed: %s\n", strerror(errno)); return 1; } buf[short_hash_length] = '\0'; ref = buf; } } while (waitpid(pid2, &status, 0) < 0) { if (errno != EINTR) { fprintf(stderr, "waitpid failed: %s\n", strerror(errno)); return 1; } } dirty = (WEXITSTATUS(status) != 0); if (ref) { if (dirty) { printf(format, ansi_codes_dirty, ref); } else { printf(format, ansi_codes, ref); } } return 0; }