1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
|
# Description: Add git status column to detail mode. Provides additional
# command line flag -G which will render the git status
# column also in normal mode. Vim plugin users may consider
# adding the -G flag to their command override.
# Compatibility patch for the namefirst patch.
#
# Authors: Luuk van Baal
diff --git a/src/nnn.c b/src/nnn.c
index 88538787..d4af7c43 100644
--- a/src/nnn.c
+++ b/src/nnn.c
@@ -270,6 +270,25 @@
#define VFS_USED 1
#define VFS_SIZE 2
+/* Git icons */
+#ifdef NERD
+#define GIT_ADD ""
+#define GIT_DEL ""
+#define GIT_IGN ""
+#define GIT_MOD ""
+#define GIT_NEW ""
+#define GIT_NON "-"
+#define GIT_UPD ""
+#else
+#define GIT_ADD "A"
+#define GIT_DEL "D"
+#define GIT_IGN "!"
+#define GIT_MOD "M"
+#define GIT_NEW "?"
+#define GIT_NON "-"
+#define GIT_UPD "U"
+#endif
+
/* TYPE DEFINITIONS */
typedef unsigned int uint_t;
typedef unsigned char uchar_t;
@@ -294,6 +313,7 @@ typedef struct entry {
uid_t uid; /* 4 bytes */
gid_t gid; /* 4 bytes */
#endif
+ char git_status[2][5];
} *pEntry;
/* Selection marker */
@@ -349,6 +369,7 @@ typedef struct {
uint_t cliopener : 1; /* All-CLI app opener */
uint_t waitedit : 1; /* For ops that can't be detached, used EDITOR */
uint_t rollover : 1; /* Roll over at edges */
+ uint_t normalgit : 1; /* Show git status in normal mode */
} settings;
/* Non-persistent program-internal states (alphabeical order) */
@@ -404,7 +425,17 @@ static struct {
ushort_t maxnameln, maxsizeln, maxuidln, maxgidln, maxentln, uidln, gidln, printguid;
} dtls;
+typedef struct {
+ char status[2];
+ char path[PATH_MAX];
+} git_status_t;
+
/* GLOBALS */
+struct {
+ bool show;
+ size_t len;
+ git_status_t *statuses;
+} git_statuses;
/* Configuration, contexts */
static settings cfg = {
@@ -3804,6 +3835,47 @@ static int get_kv_key(kv *kvarr, char *val, uchar_t max, uchar_t id)
return -1;
}
+static size_t get_git_statuses(const char *path)
+{
+ static char gst[] = "git -c core.quotePath= status -s --no-renames --ignored=matching -unormal . 2>/dev/null";
+ FILE *fp = popen(gst, "r");
+ char status[PATH_MAX];
+ size_t pathindex, i = -1;
+ git_statuses.show = FALSE;
+
+ while (fgets(status, PATH_MAX, fp)) {
+ pathindex = (status[3] == '"') ? 4 : 3;
+ if (!cfg.showhidden && status[pathindex] == '.')
+ continue;
+ status[xstrlen(status) - pathindex + 2] = '\0';
+ git_statuses.statuses = xrealloc(git_statuses.statuses, sizeof(git_status_t) * (++i + 1));
+ git_statuses.statuses[i].status[0] = status[0];
+ git_statuses.statuses[i].status[1] = status[1];
+ mkpath(path, status + pathindex, git_statuses.statuses[i].path);
+ }
+
+ pclose(fp);
+ return (i + 1);
+}
+
+static void set_git_status(char status[][5], uint_t nr)
+{
+ for (int j = 0; j < 2; j++) {
+ if (status[j][0] == '-')
+ switch (git_statuses.statuses[nr].status[j]) {
+ case ' ': xstrsncpy(status[j], GIT_NON, 4); break;
+ case 'M': xstrsncpy(status[j], GIT_MOD, 4); break;
+ case 'A': xstrsncpy(status[j], GIT_ADD, 4); break;
+ case '?': xstrsncpy(status[j], GIT_NEW, 4); break;
+ case '!': xstrsncpy(status[j], GIT_IGN, 4); break;
+ case 'D': xstrsncpy(status[j], GIT_DEL, 4); break;
+ case 'U': xstrsncpy(status[j], GIT_UPD, 4); break;
+ }
+ }
+ if (git_statuses.statuses[nr].status[1] != '!')
+ git_statuses.show = TRUE;
+}
+
static void resetdircolor(int flags)
{
/* Directories are always shown on top, clear the color when moving to first file */
@@ -4104,6 +4176,9 @@ static void printent(const struct entry *ent, uint_t namecols, bool sel)
int attrs = 0, namelen;
uchar_t color_pair = get_color_pair_name_ind(ent, &ind, &attrs);
+ if (git_statuses.show && (cfg.showdetail || cfg.normalgit))
+ printw(" %s%s", ent->git_status[0], ent->git_status[1]);
+
addch((ent->flags & FILE_SELECTED) ? '+' | A_REVERSE | A_BOLD : ' ');
if (g_state.oldcolor)
@@ -5598,6 +5673,11 @@ static int dentfill(char *path, struct entry **ppdents)
attron(COLOR_PAIR(cfg.curctx + 1));
}
+ char linkpath[PATH_MAX];
+ if ((git_statuses.len = get_git_statuses(path)))
+ if (!realpath(path, linkpath))
+ printwarn(NULL);
+
#if _POSIX_C_SOURCE >= 200112L
posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL);
#endif
@@ -5798,6 +5878,29 @@ static int dentfill(char *path, struct entry **ppdents)
#endif
}
+ if (git_statuses.len) {
+ char dentpath[PATH_MAX];
+ size_t pathlen = mkpath(linkpath, dentp->name, dentpath);
+ dentp->git_status[0][0] = dentp->git_status[1][0] = '-';
+ dentp->git_status[0][1] = dentp->git_status[1][1] = '\0';
+
+ if (dentp->flags & DIR_OR_DIRLNK) {
+ char prefix[PATH_MAX];
+ memccpy(prefix, dentpath, '\0', PATH_MAX);
+ prefix[pathlen - 1] = '/';
+
+ for (size_t i = 0; i < git_statuses.len; ++i)
+ if (is_prefix(git_statuses.statuses[i].path, prefix, pathlen))
+ set_git_status(dentp->git_status, i);
+ } else {
+ for (size_t i = 0; i < git_statuses.len; ++i)
+ if (!xstrcmp(git_statuses.statuses[i].path, dentpath)) {
+ set_git_status(dentp->git_status, i);
+ break;
+ }
+ }
+ }
+
++ndents;
} while ((dp = readdir(dirp)));
@@ -6362,7 +6465,8 @@ static int adjust_cols(int n)
cfg.showdetail ^= 1;
else /* 2 more accounted for below */
n -= (dtls.maxentln - 2 - dtls.maxnameln);
- }
+ } else if (cfg.normalgit && git_statuses.show)
+ n -= 3;
/* 2 columns for preceding space and indicator */
return (n - 2);
@@ -6517,7 +6621,7 @@ static void redraw(char *path)
}
#endif
}
- dtls.maxentln = dtls.maxnameln + dtls.maxsizeln + (dtls.printguid ? (dtls.maxuidln + dtls.maxgidln + 29) : 26);
+ dtls.maxentln = dtls.maxnameln + dtls.maxsizeln + (dtls.printguid ? (dtls.maxuidln + dtls.maxgidln + 3) : 0) + (git_statuses.show ? 29 : 26);
}
ncols = adjust_cols(ncols);
@@ -8153,6 +8257,7 @@ static void usage(void)
" -F val fifo mode [0:preview 1:explore]\n"
#endif
" -g regex filters\n"
+ " -G always show git status\n"
" -H show hidden files\n"
" -i show current file info\n"
" -J no auto-advance on selection\n"
@@ -8292,6 +8397,7 @@ static void cleanup(void)
fflush(stdout);
}
#endif
+ free(git_statuses.statuses);
free(selpath);
free(plgpath);
free(cfgpath);
@@ -8336,7 +8442,7 @@ int main(int argc, char *argv[])
while ((opt = (env_opts_id > 0
? env_opts[--env_opts_id]
- : getopt(argc, argv, "aAb:BcCdDeEfF:gHiJKl:nNop:P:QrRs:St:T:uUVx0h"))) != -1) {
+ : getopt(argc, argv, "aAb:BcCdDeEfF:gGHiJKl:nNop:P:QrRs:St:T:uUVx0h"))) != -1) {
switch (opt) {
#ifndef NOFIFO
case 'a':
@@ -8390,6 +8496,9 @@ int main(int argc, char *argv[])
cfg.regex = 1;
filterfn = &visible_re;
break;
+ case 'G':
+ cfg.normalgit = 1;
+ break;
case 'H':
cfg.showhidden = 1;
break;
|