]>
Commit | Line | Data |
---|---|---|
c91f0d92 JK |
1 | #include "wt-status.h" |
2 | #include "color.h" | |
3 | #include "cache.h" | |
4 | #include "object.h" | |
5 | #include "dir.h" | |
6 | #include "commit.h" | |
7 | #include "diff.h" | |
8 | #include "revision.h" | |
9 | #include "diffcore.h" | |
10 | ||
11 | int wt_status_use_color = 0; | |
12 | static char wt_status_colors[][COLOR_MAXLEN] = { | |
13 | "", /* WT_STATUS_HEADER: normal */ | |
14 | "\033[32m", /* WT_STATUS_UPDATED: green */ | |
15 | "\033[31m", /* WT_STATUS_CHANGED: red */ | |
16 | "\033[31m", /* WT_STATUS_UNTRACKED: red */ | |
17 | }; | |
18 | ||
19 | static int parse_status_slot(const char *var, int offset) | |
20 | { | |
21 | if (!strcasecmp(var+offset, "header")) | |
22 | return WT_STATUS_HEADER; | |
23 | if (!strcasecmp(var+offset, "updated")) | |
24 | return WT_STATUS_UPDATED; | |
25 | if (!strcasecmp(var+offset, "changed")) | |
26 | return WT_STATUS_CHANGED; | |
27 | if (!strcasecmp(var+offset, "untracked")) | |
28 | return WT_STATUS_UNTRACKED; | |
29 | die("bad config variable '%s'", var); | |
30 | } | |
31 | ||
32 | static const char* color(int slot) | |
33 | { | |
34 | return wt_status_use_color ? wt_status_colors[slot] : ""; | |
35 | } | |
36 | ||
37 | void wt_status_prepare(struct wt_status *s) | |
38 | { | |
39 | unsigned char sha1[20]; | |
40 | const char *head; | |
41 | ||
42 | s->is_initial = get_sha1("HEAD", sha1) ? 1 : 0; | |
43 | ||
44 | head = resolve_ref(git_path("HEAD"), sha1, 0); | |
45 | s->branch = head ? | |
46 | strdup(head + strlen(get_git_dir()) + 1) : | |
47 | NULL; | |
48 | ||
49 | s->reference = "HEAD"; | |
50 | s->amend = 0; | |
51 | s->verbose = 0; | |
52 | s->commitable = 0; | |
2074cb0a | 53 | s->untracked = 0; |
c91f0d92 JK |
54 | } |
55 | ||
56 | static void wt_status_print_header(const char *main, const char *sub) | |
57 | { | |
58 | const char *c = color(WT_STATUS_HEADER); | |
59 | color_printf_ln(c, "# %s:", main); | |
60 | color_printf_ln(c, "# (%s)", sub); | |
61 | color_printf_ln(c, "#"); | |
62 | } | |
63 | ||
64 | static void wt_status_print_trailer(void) | |
65 | { | |
66 | color_printf_ln(color(WT_STATUS_HEADER), "#"); | |
67 | } | |
68 | ||
69 | static void wt_status_print_filepair(int t, struct diff_filepair *p) | |
70 | { | |
71 | const char *c = color(t); | |
72 | color_printf(color(WT_STATUS_HEADER), "#\t"); | |
73 | switch (p->status) { | |
74 | case DIFF_STATUS_ADDED: | |
75 | color_printf(c, "new file: %s", p->one->path); break; | |
76 | case DIFF_STATUS_COPIED: | |
77 | color_printf(c, "copied: %s -> %s", | |
78 | p->one->path, p->two->path); | |
79 | break; | |
80 | case DIFF_STATUS_DELETED: | |
db830b4f | 81 | color_printf(c, "deleted: %s", p->one->path); break; |
c91f0d92 JK |
82 | case DIFF_STATUS_MODIFIED: |
83 | color_printf(c, "modified: %s", p->one->path); break; | |
84 | case DIFF_STATUS_RENAMED: | |
85 | color_printf(c, "renamed: %s -> %s", | |
86 | p->one->path, p->two->path); | |
87 | break; | |
88 | case DIFF_STATUS_TYPE_CHANGED: | |
89 | color_printf(c, "typechange: %s", p->one->path); break; | |
90 | case DIFF_STATUS_UNKNOWN: | |
91 | color_printf(c, "unknown: %s", p->one->path); break; | |
92 | case DIFF_STATUS_UNMERGED: | |
93 | color_printf(c, "unmerged: %s", p->one->path); break; | |
94 | default: | |
95 | die("bug: unhandled diff status %c", p->status); | |
96 | } | |
97 | printf("\n"); | |
98 | } | |
99 | ||
100 | static void wt_status_print_updated_cb(struct diff_queue_struct *q, | |
101 | struct diff_options *options, | |
102 | void *data) | |
103 | { | |
104 | struct wt_status *s = data; | |
105 | int shown_header = 0; | |
106 | int i; | |
107 | if (q->nr) { | |
108 | } | |
109 | for (i = 0; i < q->nr; i++) { | |
110 | if (q->queue[i]->status == 'U') | |
111 | continue; | |
112 | if (!shown_header) { | |
113 | wt_status_print_header("Updated but not checked in", | |
114 | "will commit"); | |
115 | s->commitable = 1; | |
116 | shown_header = 1; | |
117 | } | |
118 | wt_status_print_filepair(WT_STATUS_UPDATED, q->queue[i]); | |
119 | } | |
120 | if (shown_header) | |
121 | wt_status_print_trailer(); | |
122 | } | |
123 | ||
124 | static void wt_status_print_changed_cb(struct diff_queue_struct *q, | |
125 | struct diff_options *options, | |
126 | void *data) | |
127 | { | |
128 | int i; | |
129 | if (q->nr) | |
130 | wt_status_print_header("Changed but not updated", | |
131 | "use git-update-index to mark for commit"); | |
132 | for (i = 0; i < q->nr; i++) | |
133 | wt_status_print_filepair(WT_STATUS_CHANGED, q->queue[i]); | |
134 | if (q->nr) | |
135 | wt_status_print_trailer(); | |
136 | } | |
137 | ||
138 | void wt_status_print_initial(struct wt_status *s) | |
139 | { | |
140 | int i; | |
141 | read_cache(); | |
142 | if (active_nr) { | |
143 | s->commitable = 1; | |
144 | wt_status_print_header("Updated but not checked in", | |
145 | "will commit"); | |
146 | } | |
147 | for (i = 0; i < active_nr; i++) { | |
148 | color_printf(color(WT_STATUS_HEADER), "#\t"); | |
149 | color_printf_ln(color(WT_STATUS_UPDATED), "new file: %s", | |
150 | active_cache[i]->name); | |
151 | } | |
152 | if (active_nr) | |
153 | wt_status_print_trailer(); | |
154 | } | |
155 | ||
156 | static void wt_status_print_updated(struct wt_status *s) | |
157 | { | |
158 | struct rev_info rev; | |
159 | const char *argv[] = { NULL, NULL, NULL }; | |
160 | argv[1] = s->reference; | |
161 | init_revisions(&rev, NULL); | |
162 | setup_revisions(2, argv, &rev, NULL); | |
163 | rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK; | |
164 | rev.diffopt.format_callback = wt_status_print_updated_cb; | |
165 | rev.diffopt.format_callback_data = s; | |
166 | rev.diffopt.detect_rename = 1; | |
167 | run_diff_index(&rev, 1); | |
168 | } | |
169 | ||
170 | static void wt_status_print_changed(struct wt_status *s) | |
171 | { | |
172 | struct rev_info rev; | |
173 | const char *argv[] = { NULL, NULL }; | |
174 | init_revisions(&rev, ""); | |
175 | setup_revisions(1, argv, &rev, NULL); | |
176 | rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK; | |
177 | rev.diffopt.format_callback = wt_status_print_changed_cb; | |
178 | rev.diffopt.format_callback_data = s; | |
179 | run_diff_files(&rev, 0); | |
180 | } | |
181 | ||
182 | static void wt_status_print_untracked(const struct wt_status *s) | |
183 | { | |
184 | struct dir_struct dir; | |
185 | const char *x; | |
186 | int i; | |
187 | int shown_header = 0; | |
188 | ||
189 | memset(&dir, 0, sizeof(dir)); | |
190 | ||
191 | dir.exclude_per_dir = ".gitignore"; | |
2074cb0a JS |
192 | if (!s->untracked) { |
193 | dir.show_other_directories = 1; | |
194 | dir.hide_empty_directories = 1; | |
195 | } | |
c91f0d92 JK |
196 | x = git_path("info/exclude"); |
197 | if (file_exists(x)) | |
198 | add_excludes_from_file(&dir, x); | |
199 | ||
200 | read_directory(&dir, ".", "", 0); | |
201 | for(i = 0; i < dir.nr; i++) { | |
202 | /* check for matching entry, which is unmerged; lifted from | |
203 | * builtin-ls-files:show_other_files */ | |
204 | struct dir_entry *ent = dir.entries[i]; | |
205 | int pos = cache_name_pos(ent->name, ent->len); | |
206 | struct cache_entry *ce; | |
207 | if (0 <= pos) | |
208 | die("bug in wt_status_print_untracked"); | |
209 | pos = -pos - 1; | |
210 | if (pos < active_nr) { | |
211 | ce = active_cache[pos]; | |
212 | if (ce_namelen(ce) == ent->len && | |
213 | !memcmp(ce->name, ent->name, ent->len)) | |
214 | continue; | |
215 | } | |
216 | if (!shown_header) { | |
217 | wt_status_print_header("Untracked files", | |
218 | "use \"git add\" to add to commit"); | |
219 | shown_header = 1; | |
220 | } | |
221 | color_printf(color(WT_STATUS_HEADER), "#\t"); | |
222 | color_printf_ln(color(WT_STATUS_UNTRACKED), "%.*s", | |
223 | ent->len, ent->name); | |
224 | } | |
225 | } | |
226 | ||
227 | static void wt_status_print_verbose(struct wt_status *s) | |
228 | { | |
229 | struct rev_info rev; | |
230 | const char *argv[] = { NULL, NULL, NULL }; | |
231 | argv[1] = s->reference; | |
232 | init_revisions(&rev, NULL); | |
233 | setup_revisions(2, argv, &rev, NULL); | |
234 | rev.diffopt.output_format |= DIFF_FORMAT_PATCH; | |
235 | rev.diffopt.detect_rename = 1; | |
236 | run_diff_index(&rev, 1); | |
237 | } | |
238 | ||
239 | void wt_status_print(struct wt_status *s) | |
240 | { | |
241 | if (s->branch && strcmp(s->branch, "refs/heads/master")) | |
242 | color_printf_ln(color(WT_STATUS_HEADER), | |
243 | "# On branch %s", s->branch); | |
244 | ||
245 | if (s->is_initial) { | |
246 | color_printf_ln(color(WT_STATUS_HEADER), "#"); | |
247 | color_printf_ln(color(WT_STATUS_HEADER), "# Initial commit"); | |
248 | color_printf_ln(color(WT_STATUS_HEADER), "#"); | |
249 | wt_status_print_initial(s); | |
250 | } | |
251 | else { | |
252 | wt_status_print_updated(s); | |
253 | discard_cache(); | |
254 | } | |
255 | ||
256 | wt_status_print_changed(s); | |
257 | wt_status_print_untracked(s); | |
258 | ||
259 | if (s->verbose && !s->is_initial) | |
260 | wt_status_print_verbose(s); | |
261 | if (!s->commitable) | |
262 | printf("%s\n", s->amend ? "# No changes" : "nothing to commit"); | |
263 | } | |
264 | ||
265 | int git_status_config(const char *k, const char *v) | |
266 | { | |
267 | if (!strcmp(k, "status.color")) { | |
268 | wt_status_use_color = git_config_colorbool(k, v); | |
269 | return 0; | |
270 | } | |
271 | if (!strncmp(k, "status.color.", 13)) { | |
272 | int slot = parse_status_slot(k, 13); | |
273 | color_parse(v, k, wt_status_colors[slot]); | |
274 | } | |
275 | return git_default_config(k, v); | |
276 | } |