Coverage Report

Created: 2024-09-08 06:24

/src/git/wt-status.c
Line
Count
Source (jump to first uncovered line)
1
#define USE_THE_REPOSITORY_VARIABLE
2
3
#include "git-compat-util.h"
4
#include "advice.h"
5
#include "wt-status.h"
6
#include "object.h"
7
#include "dir.h"
8
#include "commit.h"
9
#include "diff.h"
10
#include "environment.h"
11
#include "gettext.h"
12
#include "hash.h"
13
#include "hex.h"
14
#include "object-name.h"
15
#include "path.h"
16
#include "revision.h"
17
#include "diffcore.h"
18
#include "quote.h"
19
#include "run-command.h"
20
#include "strvec.h"
21
#include "remote.h"
22
#include "refs.h"
23
#include "submodule.h"
24
#include "column.h"
25
#include "read-cache.h"
26
#include "setup.h"
27
#include "strbuf.h"
28
#include "trace.h"
29
#include "trace2.h"
30
#include "tree.h"
31
#include "utf8.h"
32
#include "worktree.h"
33
#include "lockfile.h"
34
#include "sequencer.h"
35
#include "fsmonitor-settings.h"
36
37
0
#define AB_DELAY_WARNING_IN_MS (2 * 1000)
38
82
#define UF_DELAY_WARNING_IN_MS (2 * 1000)
39
40
static const char cut_line[] =
41
"------------------------ >8 ------------------------\n";
42
43
static char default_wt_status_colors[][COLOR_MAXLEN] = {
44
  GIT_COLOR_NORMAL, /* WT_STATUS_HEADER */
45
  GIT_COLOR_GREEN,  /* WT_STATUS_UPDATED */
46
  GIT_COLOR_RED,    /* WT_STATUS_CHANGED */
47
  GIT_COLOR_RED,    /* WT_STATUS_UNTRACKED */
48
  GIT_COLOR_RED,    /* WT_STATUS_NOBRANCH */
49
  GIT_COLOR_RED,    /* WT_STATUS_UNMERGED */
50
  GIT_COLOR_GREEN,  /* WT_STATUS_LOCAL_BRANCH */
51
  GIT_COLOR_RED,    /* WT_STATUS_REMOTE_BRANCH */
52
  GIT_COLOR_NIL,    /* WT_STATUS_ONBRANCH */
53
};
54
55
static const char *color(int slot, struct wt_status *s)
56
7.13k
{
57
7.13k
  const char *c = "";
58
7.13k
  if (want_color(s->use_color))
59
0
    c = s->color_palette[slot];
60
7.13k
  if (slot == WT_STATUS_ONBRANCH && color_is_nil(c))
61
0
    c = s->color_palette[WT_STATUS_HEADER];
62
7.13k
  return c;
63
7.13k
}
64
65
static void status_vprintf(struct wt_status *s, int at_bol, const char *color,
66
    const char *fmt, va_list ap, const char *trail)
67
7.21k
{
68
7.21k
  struct strbuf sb = STRBUF_INIT;
69
7.21k
  struct strbuf linebuf = STRBUF_INIT;
70
7.21k
  const char *line, *eol;
71
72
7.21k
  strbuf_vaddf(&sb, fmt, ap);
73
7.21k
  if (!sb.len) {
74
164
    if (s->display_comment_prefix) {
75
0
      strbuf_addstr(&sb, comment_line_str);
76
0
      if (!trail)
77
0
        strbuf_addch(&sb, ' ');
78
0
    }
79
164
    color_print_strbuf(s->fp, color, &sb);
80
164
    if (trail)
81
82
      fprintf(s->fp, "%s", trail);
82
164
    strbuf_release(&sb);
83
164
    return;
84
164
  }
85
10.4k
  for (line = sb.buf; *line; line = eol + 1) {
86
7.05k
    eol = strchr(line, '\n');
87
88
7.05k
    strbuf_reset(&linebuf);
89
7.05k
    if (at_bol && s->display_comment_prefix) {
90
0
      strbuf_addstr(&linebuf, comment_line_str);
91
0
      if (*line != '\n' && *line != '\t')
92
0
        strbuf_addch(&linebuf, ' ');
93
0
    }
94
7.05k
    if (eol)
95
3.44k
      strbuf_add(&linebuf, line, eol - line);
96
3.60k
    else
97
3.60k
      strbuf_addstr(&linebuf, line);
98
7.05k
    color_print_strbuf(s->fp, color, &linebuf);
99
7.05k
    if (eol)
100
3.44k
      fprintf(s->fp, "\n");
101
3.60k
    else
102
3.60k
      break;
103
3.44k
    at_bol = 1;
104
3.44k
  }
105
7.05k
  if (trail)
106
164
    fprintf(s->fp, "%s", trail);
107
7.05k
  strbuf_release(&linebuf);
108
7.05k
  strbuf_release(&sb);
109
7.05k
}
110
111
void status_printf_ln(struct wt_status *s, const char *color,
112
      const char *fmt, ...)
113
246
{
114
246
  va_list ap;
115
116
246
  va_start(ap, fmt);
117
246
  status_vprintf(s, 1, color, fmt, ap, "\n");
118
246
  va_end(ap);
119
246
}
120
121
void status_printf(struct wt_status *s, const char *color,
122
      const char *fmt, ...)
123
3.44k
{
124
3.44k
  va_list ap;
125
126
3.44k
  va_start(ap, fmt);
127
3.44k
  status_vprintf(s, 1, color, fmt, ap, NULL);
128
3.44k
  va_end(ap);
129
3.44k
}
130
131
__attribute__((format (printf, 3, 4)))
132
static void status_printf_more(struct wt_status *s, const char *color,
133
             const char *fmt, ...)
134
3.52k
{
135
3.52k
  va_list ap;
136
137
3.52k
  va_start(ap, fmt);
138
3.52k
  status_vprintf(s, 0, color, fmt, ap, NULL);
139
3.52k
  va_end(ap);
140
3.52k
}
141
142
void wt_status_prepare(struct repository *r, struct wt_status *s)
143
9.16k
{
144
9.16k
  memset(s, 0, sizeof(*s));
145
9.16k
  s->repo = r;
146
9.16k
  memcpy(s->color_palette, default_wt_status_colors,
147
9.16k
         sizeof(default_wt_status_colors));
148
9.16k
  s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
149
9.16k
  s->use_color = -1;
150
9.16k
  s->relative_paths = 1;
151
9.16k
  s->branch = refs_resolve_refdup(get_main_ref_store(the_repository),
152
9.16k
          "HEAD", 0, NULL, NULL);
153
9.16k
  s->reference = "HEAD";
154
9.16k
  s->fp = stdout;
155
9.16k
  s->index_file = get_index_file();
156
9.16k
  s->change.strdup_strings = 1;
157
9.16k
  s->untracked.strdup_strings = 1;
158
9.16k
  s->ignored.strdup_strings = 1;
159
9.16k
  s->show_branch = -1;  /* unspecified */
160
9.16k
  s->show_stash = 0;
161
9.16k
  s->ahead_behind_flags = AHEAD_BEHIND_UNSPECIFIED;
162
9.16k
  s->display_comment_prefix = 0;
163
9.16k
  s->detect_rename = -1;
164
9.16k
  s->rename_score = -1;
165
9.16k
  s->rename_limit = -1;
166
9.16k
}
167
168
static void wt_longstatus_print_unmerged_header(struct wt_status *s)
169
0
{
170
0
  int i;
171
0
  int del_mod_conflict = 0;
172
0
  int both_deleted = 0;
173
0
  int not_deleted = 0;
174
0
  const char *c = color(WT_STATUS_HEADER, s);
175
176
0
  status_printf_ln(s, c, _("Unmerged paths:"));
177
178
0
  for (i = 0; i < s->change.nr; i++) {
179
0
    struct string_list_item *it = &(s->change.items[i]);
180
0
    struct wt_status_change_data *d = it->util;
181
182
0
    switch (d->stagemask) {
183
0
    case 0:
184
0
      break;
185
0
    case 1:
186
0
      both_deleted = 1;
187
0
      break;
188
0
    case 3:
189
0
    case 5:
190
0
      del_mod_conflict = 1;
191
0
      break;
192
0
    default:
193
0
      not_deleted = 1;
194
0
      break;
195
0
    }
196
0
  }
197
198
0
  if (!s->hints)
199
0
    return;
200
0
  if (s->whence != FROM_COMMIT)
201
0
    ;
202
0
  else if (!s->is_initial) {
203
0
    if (!strcmp(s->reference, "HEAD"))
204
0
      status_printf_ln(s, c,
205
0
           _("  (use \"git restore --staged <file>...\" to unstage)"));
206
0
    else
207
0
      status_printf_ln(s, c,
208
0
           _("  (use \"git restore --source=%s --staged <file>...\" to unstage)"),
209
0
           s->reference);
210
0
  } else
211
0
    status_printf_ln(s, c, _("  (use \"git rm --cached <file>...\" to unstage)"));
212
213
0
  if (!both_deleted) {
214
0
    if (!del_mod_conflict)
215
0
      status_printf_ln(s, c, _("  (use \"git add <file>...\" to mark resolution)"));
216
0
    else
217
0
      status_printf_ln(s, c, _("  (use \"git add/rm <file>...\" as appropriate to mark resolution)"));
218
0
  } else if (!del_mod_conflict && !not_deleted) {
219
0
    status_printf_ln(s, c, _("  (use \"git rm <file>...\" to mark resolution)"));
220
0
  } else {
221
0
    status_printf_ln(s, c, _("  (use \"git add/rm <file>...\" as appropriate to mark resolution)"));
222
0
  }
223
0
}
224
225
static void wt_longstatus_print_cached_header(struct wt_status *s)
226
0
{
227
0
  const char *c = color(WT_STATUS_HEADER, s);
228
229
0
  status_printf_ln(s, c, _("Changes to be committed:"));
230
0
  if (!s->hints)
231
0
    return;
232
0
  if (s->whence != FROM_COMMIT)
233
0
    ; /* NEEDSWORK: use "git reset --unresolve"??? */
234
0
  else if (!s->is_initial) {
235
0
    if (!strcmp(s->reference, "HEAD"))
236
0
      status_printf_ln(s, c
237
0
           , _("  (use \"git restore --staged <file>...\" to unstage)"));
238
0
    else
239
0
      status_printf_ln(s, c,
240
0
           _("  (use \"git restore --source=%s --staged <file>...\" to unstage)"),
241
0
           s->reference);
242
0
  } else
243
0
    status_printf_ln(s, c, _("  (use \"git rm --cached <file>...\" to unstage)"));
244
0
}
245
246
static void wt_longstatus_print_dirty_header(struct wt_status *s,
247
               int has_deleted,
248
               int has_dirty_submodules)
249
0
{
250
0
  const char *c = color(WT_STATUS_HEADER, s);
251
252
0
  status_printf_ln(s, c, _("Changes not staged for commit:"));
253
0
  if (!s->hints)
254
0
    return;
255
0
  if (!has_deleted)
256
0
    status_printf_ln(s, c, _("  (use \"git add <file>...\" to update what will be committed)"));
257
0
  else
258
0
    status_printf_ln(s, c, _("  (use \"git add/rm <file>...\" to update what will be committed)"));
259
0
  status_printf_ln(s, c, _("  (use \"git restore <file>...\" to discard changes in working directory)"));
260
0
  if (has_dirty_submodules)
261
0
    status_printf_ln(s, c, _("  (commit or discard the untracked or modified content in submodules)"));
262
0
}
263
264
static void wt_longstatus_print_other_header(struct wt_status *s,
265
               const char *what,
266
               const char *how)
267
82
{
268
82
  const char *c = color(WT_STATUS_HEADER, s);
269
82
  status_printf_ln(s, c, "%s:", what);
270
82
  if (!s->hints)
271
0
    return;
272
82
  status_printf_ln(s, c, _("  (use \"git %s <file>...\" to include in what will be committed)"), how);
273
82
}
274
275
static void wt_longstatus_print_trailer(struct wt_status *s)
276
0
{
277
0
  status_printf_ln(s, color(WT_STATUS_HEADER, s), "%s", "");
278
0
}
279
280
static const char *wt_status_unmerged_status_string(int stagemask)
281
0
{
282
0
  switch (stagemask) {
283
0
  case 1:
284
0
    return _("both deleted:");
285
0
  case 2:
286
0
    return _("added by us:");
287
0
  case 3:
288
0
    return _("deleted by them:");
289
0
  case 4:
290
0
    return _("added by them:");
291
0
  case 5:
292
0
    return _("deleted by us:");
293
0
  case 6:
294
0
    return _("both added:");
295
0
  case 7:
296
0
    return _("both modified:");
297
0
  default:
298
0
    BUG("unhandled unmerged status %x", stagemask);
299
0
  }
300
0
}
301
302
static const char *wt_status_diff_status_string(int status)
303
0
{
304
0
  switch (status) {
305
0
  case DIFF_STATUS_ADDED:
306
0
    return _("new file:");
307
0
  case DIFF_STATUS_COPIED:
308
0
    return _("copied:");
309
0
  case DIFF_STATUS_DELETED:
310
0
    return _("deleted:");
311
0
  case DIFF_STATUS_MODIFIED:
312
0
    return _("modified:");
313
0
  case DIFF_STATUS_RENAMED:
314
0
    return _("renamed:");
315
0
  case DIFF_STATUS_TYPE_CHANGED:
316
0
    return _("typechange:");
317
0
  case DIFF_STATUS_UNKNOWN:
318
0
    return _("unknown:");
319
0
  case DIFF_STATUS_UNMERGED:
320
0
    return _("unmerged:");
321
0
  default:
322
0
    return NULL;
323
0
  }
324
0
}
325
326
static int maxwidth(const char *(*label)(int), int minval, int maxval)
327
0
{
328
0
  int result = 0, i;
329
330
0
  for (i = minval; i <= maxval; i++) {
331
0
    const char *s = label(i);
332
0
    int len = s ? utf8_strwidth(s) : 0;
333
0
    if (len > result)
334
0
      result = len;
335
0
  }
336
0
  return result;
337
0
}
338
339
static void wt_longstatus_print_unmerged_data(struct wt_status *s,
340
                struct string_list_item *it)
341
0
{
342
0
  const char *c = color(WT_STATUS_UNMERGED, s);
343
0
  struct wt_status_change_data *d = it->util;
344
0
  struct strbuf onebuf = STRBUF_INIT;
345
0
  static char *padding;
346
0
  static int label_width;
347
0
  const char *one, *how;
348
0
  int len;
349
350
0
  if (!padding) {
351
0
    label_width = maxwidth(wt_status_unmerged_status_string, 1, 7);
352
0
    label_width += strlen(" ");
353
0
    padding = xmallocz(label_width);
354
0
    memset(padding, ' ', label_width);
355
0
  }
356
357
0
  one = quote_path(it->string, s->prefix, &onebuf, 0);
358
0
  status_printf(s, color(WT_STATUS_HEADER, s), "\t");
359
360
0
  how = wt_status_unmerged_status_string(d->stagemask);
361
0
  len = label_width - utf8_strwidth(how);
362
0
  status_printf_more(s, c, "%s%.*s%s\n", how, len, padding, one);
363
0
  strbuf_release(&onebuf);
364
0
}
365
366
static void wt_longstatus_print_change_data(struct wt_status *s,
367
              int change_type,
368
              struct string_list_item *it)
369
0
{
370
0
  struct wt_status_change_data *d = it->util;
371
0
  const char *c = color(change_type, s);
372
0
  int status;
373
0
  char *one_name;
374
0
  char *two_name;
375
0
  const char *one, *two;
376
0
  struct strbuf onebuf = STRBUF_INIT, twobuf = STRBUF_INIT;
377
0
  struct strbuf extra = STRBUF_INIT;
378
0
  static char *padding;
379
0
  static int label_width;
380
0
  const char *what;
381
0
  int len;
382
383
0
  if (!padding) {
384
    /* If DIFF_STATUS_* uses outside the range [A..Z], we're in trouble */
385
0
    label_width = maxwidth(wt_status_diff_status_string, 'A', 'Z');
386
0
    label_width += strlen(" ");
387
0
    padding = xmallocz(label_width);
388
0
    memset(padding, ' ', label_width);
389
0
  }
390
391
0
  one_name = two_name = it->string;
392
0
  switch (change_type) {
393
0
  case WT_STATUS_UPDATED:
394
0
    status = d->index_status;
395
0
    break;
396
0
  case WT_STATUS_CHANGED:
397
0
    if (d->new_submodule_commits || d->dirty_submodule) {
398
0
      strbuf_addstr(&extra, " (");
399
0
      if (d->new_submodule_commits)
400
0
        strbuf_addstr(&extra, _("new commits, "));
401
0
      if (d->dirty_submodule & DIRTY_SUBMODULE_MODIFIED)
402
0
        strbuf_addstr(&extra, _("modified content, "));
403
0
      if (d->dirty_submodule & DIRTY_SUBMODULE_UNTRACKED)
404
0
        strbuf_addstr(&extra, _("untracked content, "));
405
0
      strbuf_setlen(&extra, extra.len - 2);
406
0
      strbuf_addch(&extra, ')');
407
0
    }
408
0
    status = d->worktree_status;
409
0
    break;
410
0
  default:
411
0
    BUG("unhandled change_type %d in wt_longstatus_print_change_data",
412
0
        change_type);
413
0
  }
414
415
  /*
416
   * Only pick up the rename it's relevant. If the rename is for
417
   * the changed section and we're printing the updated section,
418
   * ignore it.
419
   */
420
0
  if (d->rename_status == status)
421
0
    one_name = d->rename_source;
422
423
0
  one = quote_path(one_name, s->prefix, &onebuf, 0);
424
0
  two = quote_path(two_name, s->prefix, &twobuf, 0);
425
426
0
  status_printf(s, color(WT_STATUS_HEADER, s), "\t");
427
0
  what = wt_status_diff_status_string(status);
428
0
  if (!what)
429
0
    BUG("unhandled diff status %c", status);
430
0
  len = label_width - utf8_strwidth(what);
431
0
  assert(len >= 0);
432
0
  if (one_name != two_name)
433
0
    status_printf_more(s, c, "%s%.*s%s -> %s",
434
0
           what, len, padding, one, two);
435
0
  else
436
0
    status_printf_more(s, c, "%s%.*s%s",
437
0
           what, len, padding, one);
438
0
  if (extra.len) {
439
0
    status_printf_more(s, color(WT_STATUS_HEADER, s), "%s", extra.buf);
440
0
    strbuf_release(&extra);
441
0
  }
442
0
  status_printf_more(s, GIT_COLOR_NORMAL, "\n");
443
0
  strbuf_release(&onebuf);
444
0
  strbuf_release(&twobuf);
445
0
}
446
447
static char short_submodule_status(struct wt_status_change_data *d)
448
0
{
449
0
  if (d->new_submodule_commits)
450
0
    return 'M';
451
0
  if (d->dirty_submodule & DIRTY_SUBMODULE_MODIFIED)
452
0
    return 'm';
453
0
  if (d->dirty_submodule & DIRTY_SUBMODULE_UNTRACKED)
454
0
    return '?';
455
0
  return d->worktree_status;
456
0
}
457
458
static void wt_status_collect_changed_cb(struct diff_queue_struct *q,
459
           struct diff_options *options UNUSED,
460
           void *data)
461
0
{
462
0
  struct wt_status *s = data;
463
0
  int i;
464
465
0
  if (!q->nr)
466
0
    return;
467
0
  s->workdir_dirty = 1;
468
0
  for (i = 0; i < q->nr; i++) {
469
0
    struct diff_filepair *p;
470
0
    struct string_list_item *it;
471
0
    struct wt_status_change_data *d;
472
473
0
    p = q->queue[i];
474
0
    it = string_list_insert(&s->change, p->two->path);
475
0
    d = it->util;
476
0
    if (!d) {
477
0
      CALLOC_ARRAY(d, 1);
478
0
      it->util = d;
479
0
    }
480
0
    if (!d->worktree_status)
481
0
      d->worktree_status = p->status;
482
0
    if (S_ISGITLINK(p->two->mode)) {
483
0
      d->dirty_submodule = p->two->dirty_submodule;
484
0
      d->new_submodule_commits = !oideq(&p->one->oid,
485
0
                &p->two->oid);
486
0
      if (s->status_format == STATUS_FORMAT_SHORT)
487
0
        d->worktree_status = short_submodule_status(d);
488
0
    }
489
490
0
    switch (p->status) {
491
0
    case DIFF_STATUS_ADDED:
492
0
      d->mode_worktree = p->two->mode;
493
0
      break;
494
495
0
    case DIFF_STATUS_DELETED:
496
0
      d->mode_index = p->one->mode;
497
0
      oidcpy(&d->oid_index, &p->one->oid);
498
      /* mode_worktree is zero for a delete. */
499
0
      break;
500
501
0
    case DIFF_STATUS_COPIED:
502
0
    case DIFF_STATUS_RENAMED:
503
0
      if (d->rename_status)
504
0
        BUG("multiple renames on the same target? how?");
505
0
      d->rename_source = xstrdup(p->one->path);
506
0
      d->rename_score = p->score * 100 / MAX_SCORE;
507
0
      d->rename_status = p->status;
508
      /* fallthru */
509
0
    case DIFF_STATUS_MODIFIED:
510
0
    case DIFF_STATUS_TYPE_CHANGED:
511
0
    case DIFF_STATUS_UNMERGED:
512
0
      d->mode_index = p->one->mode;
513
0
      d->mode_worktree = p->two->mode;
514
0
      oidcpy(&d->oid_index, &p->one->oid);
515
0
      break;
516
517
0
    default:
518
0
      BUG("unhandled diff-files status '%c'", p->status);
519
0
      break;
520
0
    }
521
522
0
  }
523
0
}
524
525
static int unmerged_mask(struct index_state *istate, const char *path)
526
0
{
527
0
  int pos, mask;
528
0
  const struct cache_entry *ce;
529
530
0
  pos = index_name_pos(istate, path, strlen(path));
531
0
  if (0 <= pos)
532
0
    return 0;
533
534
0
  mask = 0;
535
0
  pos = -pos-1;
536
0
  while (pos < istate->cache_nr) {
537
0
    ce = istate->cache[pos++];
538
0
    if (strcmp(ce->name, path) || !ce_stage(ce))
539
0
      break;
540
0
    mask |= (1 << (ce_stage(ce) - 1));
541
0
  }
542
0
  return mask;
543
0
}
544
545
static void wt_status_collect_updated_cb(struct diff_queue_struct *q,
546
           struct diff_options *options UNUSED,
547
           void *data)
548
0
{
549
0
  struct wt_status *s = data;
550
0
  int i;
551
552
0
  for (i = 0; i < q->nr; i++) {
553
0
    struct diff_filepair *p;
554
0
    struct string_list_item *it;
555
0
    struct wt_status_change_data *d;
556
557
0
    p = q->queue[i];
558
0
    it = string_list_insert(&s->change, p->two->path);
559
0
    d = it->util;
560
0
    if (!d) {
561
0
      CALLOC_ARRAY(d, 1);
562
0
      it->util = d;
563
0
    }
564
0
    if (!d->index_status)
565
0
      d->index_status = p->status;
566
0
    switch (p->status) {
567
0
    case DIFF_STATUS_ADDED:
568
      /* Leave {mode,oid}_head zero for an add. */
569
0
      d->mode_index = p->two->mode;
570
0
      oidcpy(&d->oid_index, &p->two->oid);
571
0
      s->committable = 1;
572
0
      break;
573
0
    case DIFF_STATUS_DELETED:
574
0
      d->mode_head = p->one->mode;
575
0
      oidcpy(&d->oid_head, &p->one->oid);
576
0
      s->committable = 1;
577
      /* Leave {mode,oid}_index zero for a delete. */
578
0
      break;
579
580
0
    case DIFF_STATUS_COPIED:
581
0
    case DIFF_STATUS_RENAMED:
582
0
      if (d->rename_status)
583
0
        BUG("multiple renames on the same target? how?");
584
0
      d->rename_source = xstrdup(p->one->path);
585
0
      d->rename_score = p->score * 100 / MAX_SCORE;
586
0
      d->rename_status = p->status;
587
      /* fallthru */
588
0
    case DIFF_STATUS_MODIFIED:
589
0
    case DIFF_STATUS_TYPE_CHANGED:
590
0
      d->mode_head = p->one->mode;
591
0
      d->mode_index = p->two->mode;
592
0
      oidcpy(&d->oid_head, &p->one->oid);
593
0
      oidcpy(&d->oid_index, &p->two->oid);
594
0
      s->committable = 1;
595
0
      break;
596
0
    case DIFF_STATUS_UNMERGED:
597
0
      d->stagemask = unmerged_mask(s->repo->index,
598
0
                 p->two->path);
599
      /*
600
       * Don't bother setting {mode,oid}_{head,index} since the print
601
       * code will output the stage values directly and not use the
602
       * values in these fields.
603
       */
604
0
      break;
605
606
0
    default:
607
0
      BUG("unhandled diff-index status '%c'", p->status);
608
0
      break;
609
0
    }
610
0
  }
611
0
}
612
613
static void wt_status_collect_changes_worktree(struct wt_status *s)
614
82
{
615
82
  struct rev_info rev;
616
617
82
  repo_init_revisions(s->repo, &rev, NULL);
618
82
  setup_revisions(0, NULL, &rev, NULL);
619
82
  rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
620
82
  rev.diffopt.flags.dirty_submodules = 1;
621
82
  rev.diffopt.ita_invisible_in_index = 1;
622
82
  if (!s->show_untracked_files)
623
0
    rev.diffopt.flags.ignore_untracked_in_submodules = 1;
624
82
  if (s->ignore_submodule_arg) {
625
0
    rev.diffopt.flags.override_submodule_config = 1;
626
0
    handle_ignore_submodules_arg(&rev.diffopt, s->ignore_submodule_arg);
627
82
  } else if (!rev.diffopt.flags.ignore_submodule_set &&
628
82
      s->show_untracked_files != SHOW_NO_UNTRACKED_FILES)
629
82
    handle_ignore_submodules_arg(&rev.diffopt, "none");
630
82
  rev.diffopt.format_callback = wt_status_collect_changed_cb;
631
82
  rev.diffopt.format_callback_data = s;
632
82
  rev.diffopt.detect_rename = s->detect_rename >= 0 ? s->detect_rename : rev.diffopt.detect_rename;
633
82
  rev.diffopt.rename_limit = s->rename_limit >= 0 ? s->rename_limit : rev.diffopt.rename_limit;
634
82
  rev.diffopt.rename_score = s->rename_score >= 0 ? s->rename_score : rev.diffopt.rename_score;
635
82
  copy_pathspec(&rev.prune_data, &s->pathspec);
636
82
  run_diff_files(&rev, 0);
637
82
  release_revisions(&rev);
638
82
}
639
640
static void wt_status_collect_changes_index(struct wt_status *s)
641
82
{
642
82
  struct rev_info rev;
643
82
  struct setup_revision_opt opt;
644
645
82
  repo_init_revisions(s->repo, &rev, NULL);
646
82
  memset(&opt, 0, sizeof(opt));
647
82
  opt.def = s->is_initial ? empty_tree_oid_hex(the_repository->hash_algo) : s->reference;
648
82
  setup_revisions(0, NULL, &rev, &opt);
649
650
82
  rev.diffopt.flags.override_submodule_config = 1;
651
82
  rev.diffopt.ita_invisible_in_index = 1;
652
82
  if (s->ignore_submodule_arg) {
653
0
    handle_ignore_submodules_arg(&rev.diffopt, s->ignore_submodule_arg);
654
82
  } else {
655
    /*
656
     * Unless the user did explicitly request a submodule ignore
657
     * mode by passing a command line option we do not ignore any
658
     * changed submodule SHA-1s when comparing index and HEAD, no
659
     * matter what is configured. Otherwise the user won't be
660
     * shown any submodules manually added (and which are
661
     * staged to be committed), which would be really confusing.
662
     */
663
82
    handle_ignore_submodules_arg(&rev.diffopt, "dirty");
664
82
  }
665
666
82
  rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
667
82
  rev.diffopt.format_callback = wt_status_collect_updated_cb;
668
82
  rev.diffopt.format_callback_data = s;
669
82
  rev.diffopt.detect_rename = s->detect_rename >= 0 ? s->detect_rename : rev.diffopt.detect_rename;
670
82
  rev.diffopt.rename_limit = s->rename_limit >= 0 ? s->rename_limit : rev.diffopt.rename_limit;
671
82
  rev.diffopt.rename_score = s->rename_score >= 0 ? s->rename_score : rev.diffopt.rename_score;
672
673
  /*
674
   * The `recursive` option must be enabled to allow the diff to recurse
675
   * into subdirectories of sparse directory index entries. If it is not
676
   * enabled, a subdirectory containing file(s) with changes is reported
677
   * as "modified", rather than the modified files themselves.
678
   */
679
82
  rev.diffopt.flags.recursive = 1;
680
681
82
  copy_pathspec(&rev.prune_data, &s->pathspec);
682
82
  run_diff_index(&rev, DIFF_INDEX_CACHED);
683
82
  release_revisions(&rev);
684
82
}
685
686
static int add_file_to_list(const struct object_id *oid,
687
          struct strbuf *base, const char *path,
688
          unsigned int mode, void *context)
689
0
{
690
0
  struct string_list_item *it;
691
0
  struct wt_status_change_data *d;
692
0
  struct wt_status *s = context;
693
0
  struct strbuf full_name = STRBUF_INIT;
694
695
0
  if (S_ISDIR(mode))
696
0
    return READ_TREE_RECURSIVE;
697
698
0
  strbuf_add(&full_name, base->buf, base->len);
699
0
  strbuf_addstr(&full_name, path);
700
0
  it = string_list_insert(&s->change, full_name.buf);
701
0
  d = it->util;
702
0
  if (!d) {
703
0
    CALLOC_ARRAY(d, 1);
704
0
    it->util = d;
705
0
  }
706
707
0
  d->index_status = DIFF_STATUS_ADDED;
708
  /* Leave {mode,oid}_head zero for adds. */
709
0
  d->mode_index = mode;
710
0
  oidcpy(&d->oid_index, oid);
711
0
  s->committable = 1;
712
0
  strbuf_release(&full_name);
713
0
  return 0;
714
0
}
715
716
static void wt_status_collect_changes_initial(struct wt_status *s)
717
0
{
718
0
  struct index_state *istate = s->repo->index;
719
0
  int i;
720
721
0
  for (i = 0; i < istate->cache_nr; i++) {
722
0
    struct string_list_item *it;
723
0
    struct wt_status_change_data *d;
724
0
    const struct cache_entry *ce = istate->cache[i];
725
726
0
    if (!ce_path_match(istate, ce, &s->pathspec, NULL))
727
0
      continue;
728
0
    if (ce_intent_to_add(ce))
729
0
      continue;
730
0
    if (S_ISSPARSEDIR(ce->ce_mode)) {
731
      /*
732
       * This is a sparse directory entry, so we want to collect all
733
       * of the added files within the tree. This requires recursively
734
       * expanding the trees to find the elements that are new in this
735
       * tree and marking them with DIFF_STATUS_ADDED.
736
       */
737
0
      struct strbuf base = STRBUF_INIT;
738
0
      struct pathspec ps = { 0 };
739
0
      struct tree *tree = lookup_tree(istate->repo, &ce->oid);
740
741
0
      ps.recursive = 1;
742
0
      ps.has_wildcard = 1;
743
0
      ps.max_depth = -1;
744
745
0
      strbuf_add(&base, ce->name, ce->ce_namelen);
746
0
      read_tree_at(istate->repo, tree, &base, 0, &ps,
747
0
             add_file_to_list, s);
748
0
      continue;
749
0
    }
750
751
0
    it = string_list_insert(&s->change, ce->name);
752
0
    d = it->util;
753
0
    if (!d) {
754
0
      CALLOC_ARRAY(d, 1);
755
0
      it->util = d;
756
0
    }
757
0
    if (ce_stage(ce)) {
758
0
      d->index_status = DIFF_STATUS_UNMERGED;
759
0
      d->stagemask |= (1 << (ce_stage(ce) - 1));
760
      /*
761
       * Don't bother setting {mode,oid}_{head,index} since the print
762
       * code will output the stage values directly and not use the
763
       * values in these fields.
764
       */
765
0
      s->committable = 1;
766
0
    } else {
767
0
      d->index_status = DIFF_STATUS_ADDED;
768
      /* Leave {mode,oid}_head zero for adds. */
769
0
      d->mode_index = ce->ce_mode;
770
0
      oidcpy(&d->oid_index, &ce->oid);
771
0
      s->committable = 1;
772
0
    }
773
0
  }
774
0
}
775
776
static void wt_status_collect_untracked(struct wt_status *s)
777
82
{
778
82
  int i;
779
82
  struct dir_struct dir = DIR_INIT;
780
82
  uint64_t t_begin = getnanotime();
781
82
  struct index_state *istate = s->repo->index;
782
783
82
  if (!s->show_untracked_files)
784
0
    return;
785
786
82
  if (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES)
787
82
    dir.flags |=
788
82
      DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;
789
82
  if (s->show_ignored_mode) {
790
0
    dir.flags |= DIR_SHOW_IGNORED_TOO;
791
792
0
    if (s->show_ignored_mode == SHOW_MATCHING_IGNORED)
793
0
      dir.flags |= DIR_SHOW_IGNORED_TOO_MODE_MATCHING;
794
82
  } else {
795
82
    dir.untracked = istate->untracked;
796
82
  }
797
798
82
  setup_standard_excludes(&dir);
799
800
82
  fill_directory(&dir, istate, &s->pathspec);
801
802
3.44k
  for (i = 0; i < dir.nr; i++) {
803
3.36k
    struct dir_entry *ent = dir.entries[i];
804
3.36k
    if (index_name_is_other(istate, ent->name, ent->len))
805
3.36k
      string_list_insert(&s->untracked, ent->name);
806
3.36k
  }
807
808
82
  for (i = 0; i < dir.ignored_nr; i++) {
809
0
    struct dir_entry *ent = dir.ignored[i];
810
0
    if (index_name_is_other(istate, ent->name, ent->len))
811
0
      string_list_insert(&s->ignored, ent->name);
812
0
  }
813
814
82
  dir_clear(&dir);
815
816
82
  if (advice_enabled(ADVICE_STATUS_U_OPTION))
817
82
    s->untracked_in_ms = (getnanotime() - t_begin) / 1000000;
818
82
}
819
820
static int has_unmerged(struct wt_status *s)
821
0
{
822
0
  int i;
823
824
0
  for (i = 0; i < s->change.nr; i++) {
825
0
    struct wt_status_change_data *d;
826
0
    d = s->change.items[i].util;
827
0
    if (d->stagemask)
828
0
      return 1;
829
0
  }
830
0
  return 0;
831
0
}
832
833
void wt_status_collect(struct wt_status *s)
834
82
{
835
82
  trace2_region_enter("status", "worktrees", s->repo);
836
82
  wt_status_collect_changes_worktree(s);
837
82
  trace2_region_leave("status", "worktrees", s->repo);
838
839
82
  if (s->is_initial) {
840
0
    trace2_region_enter("status", "initial", s->repo);
841
0
    wt_status_collect_changes_initial(s);
842
0
    trace2_region_leave("status", "initial", s->repo);
843
82
  } else {
844
82
    trace2_region_enter("status", "index", s->repo);
845
82
    wt_status_collect_changes_index(s);
846
82
    trace2_region_leave("status", "index", s->repo);
847
82
  }
848
849
82
  trace2_region_enter("status", "untracked", s->repo);
850
82
  wt_status_collect_untracked(s);
851
82
  trace2_region_leave("status", "untracked", s->repo);
852
853
82
  wt_status_get_state(s->repo, &s->state, s->branch && !strcmp(s->branch, "HEAD"));
854
82
  if (s->state.merge_in_progress && !has_unmerged(s))
855
0
    s->committable = 1;
856
82
}
857
858
void wt_status_collect_free_buffers(struct wt_status *s)
859
82
{
860
82
  wt_status_state_free_buffers(&s->state);
861
82
}
862
863
void wt_status_state_free_buffers(struct wt_status_state *state)
864
82
{
865
82
  FREE_AND_NULL(state->branch);
866
82
  FREE_AND_NULL(state->onto);
867
82
  FREE_AND_NULL(state->detached_from);
868
82
  FREE_AND_NULL(state->bisecting_from);
869
82
}
870
871
static void wt_longstatus_print_unmerged(struct wt_status *s)
872
82
{
873
82
  int shown_header = 0;
874
82
  int i;
875
876
82
  for (i = 0; i < s->change.nr; i++) {
877
0
    struct wt_status_change_data *d;
878
0
    struct string_list_item *it;
879
0
    it = &(s->change.items[i]);
880
0
    d = it->util;
881
0
    if (!d->stagemask)
882
0
      continue;
883
0
    if (!shown_header) {
884
0
      wt_longstatus_print_unmerged_header(s);
885
0
      shown_header = 1;
886
0
    }
887
0
    wt_longstatus_print_unmerged_data(s, it);
888
0
  }
889
82
  if (shown_header)
890
0
    wt_longstatus_print_trailer(s);
891
892
82
}
893
894
static void wt_longstatus_print_updated(struct wt_status *s)
895
82
{
896
82
  int shown_header = 0;
897
82
  int i;
898
899
82
  for (i = 0; i < s->change.nr; i++) {
900
0
    struct wt_status_change_data *d;
901
0
    struct string_list_item *it;
902
0
    it = &(s->change.items[i]);
903
0
    d = it->util;
904
0
    if (!d->index_status ||
905
0
        d->index_status == DIFF_STATUS_UNMERGED)
906
0
      continue;
907
0
    if (!shown_header) {
908
0
      wt_longstatus_print_cached_header(s);
909
0
      shown_header = 1;
910
0
    }
911
0
    wt_longstatus_print_change_data(s, WT_STATUS_UPDATED, it);
912
0
  }
913
82
  if (shown_header)
914
0
    wt_longstatus_print_trailer(s);
915
82
}
916
917
/*
918
 * -1 : has delete
919
 *  0 : no change
920
 *  1 : some change but no delete
921
 */
922
static int wt_status_check_worktree_changes(struct wt_status *s,
923
               int *dirty_submodules)
924
82
{
925
82
  int i;
926
82
  int changes = 0;
927
928
82
  *dirty_submodules = 0;
929
930
82
  for (i = 0; i < s->change.nr; i++) {
931
0
    struct wt_status_change_data *d;
932
0
    d = s->change.items[i].util;
933
0
    if (!d->worktree_status ||
934
0
        d->worktree_status == DIFF_STATUS_UNMERGED)
935
0
      continue;
936
0
    if (!changes)
937
0
      changes = 1;
938
0
    if (d->dirty_submodule)
939
0
      *dirty_submodules = 1;
940
0
    if (d->worktree_status == DIFF_STATUS_DELETED)
941
0
      changes = -1;
942
0
  }
943
82
  return changes;
944
82
}
945
946
static void wt_longstatus_print_changed(struct wt_status *s)
947
82
{
948
82
  int i, dirty_submodules;
949
82
  int worktree_changes = wt_status_check_worktree_changes(s, &dirty_submodules);
950
951
82
  if (!worktree_changes)
952
82
    return;
953
954
0
  wt_longstatus_print_dirty_header(s, worktree_changes < 0, dirty_submodules);
955
956
0
  for (i = 0; i < s->change.nr; i++) {
957
0
    struct wt_status_change_data *d;
958
0
    struct string_list_item *it;
959
0
    it = &(s->change.items[i]);
960
0
    d = it->util;
961
0
    if (!d->worktree_status ||
962
0
        d->worktree_status == DIFF_STATUS_UNMERGED)
963
0
      continue;
964
0
    wt_longstatus_print_change_data(s, WT_STATUS_CHANGED, it);
965
0
  }
966
0
  wt_longstatus_print_trailer(s);
967
0
}
968
969
static int stash_count_refs(struct object_id *ooid UNUSED,
970
          struct object_id *noid UNUSED,
971
          const char *email UNUSED,
972
          timestamp_t timestamp UNUSED, int tz UNUSED,
973
          const char *message UNUSED, void *cb_data)
974
0
{
975
0
  int *c = cb_data;
976
0
  (*c)++;
977
0
  return 0;
978
0
}
979
980
static int count_stash_entries(void)
981
0
{
982
0
  int n = 0;
983
0
  refs_for_each_reflog_ent(get_main_ref_store(the_repository),
984
0
         "refs/stash", stash_count_refs, &n);
985
0
  return n;
986
0
}
987
988
static void wt_longstatus_print_stash_summary(struct wt_status *s)
989
0
{
990
0
  int stash_count = count_stash_entries();
991
992
0
  if (stash_count > 0)
993
0
    status_printf_ln(s, GIT_COLOR_NORMAL,
994
0
         Q_("Your stash currently has %d entry",
995
0
            "Your stash currently has %d entries", stash_count),
996
0
         stash_count);
997
0
}
998
999
static void wt_longstatus_print_submodule_summary(struct wt_status *s, int uncommitted)
1000
0
{
1001
0
  struct child_process sm_summary = CHILD_PROCESS_INIT;
1002
0
  struct strbuf cmd_stdout = STRBUF_INIT;
1003
0
  struct strbuf summary = STRBUF_INIT;
1004
0
  char *summary_content;
1005
1006
0
  strvec_pushf(&sm_summary.env, "GIT_INDEX_FILE=%s", s->index_file);
1007
1008
0
  strvec_push(&sm_summary.args, "submodule");
1009
0
  strvec_push(&sm_summary.args, "summary");
1010
0
  strvec_push(&sm_summary.args, uncommitted ? "--files" : "--cached");
1011
0
  strvec_push(&sm_summary.args, "--for-status");
1012
0
  strvec_push(&sm_summary.args, "--summary-limit");
1013
0
  strvec_pushf(&sm_summary.args, "%d", s->submodule_summary);
1014
0
  if (!uncommitted)
1015
0
    strvec_push(&sm_summary.args, s->amend ? "HEAD^" : "HEAD");
1016
1017
0
  sm_summary.git_cmd = 1;
1018
0
  sm_summary.no_stdin = 1;
1019
1020
0
  capture_command(&sm_summary, &cmd_stdout, 1024);
1021
1022
  /* prepend header, only if there's an actual output */
1023
0
  if (cmd_stdout.len) {
1024
0
    if (uncommitted)
1025
0
      strbuf_addstr(&summary, _("Submodules changed but not updated:"));
1026
0
    else
1027
0
      strbuf_addstr(&summary, _("Submodule changes to be committed:"));
1028
0
    strbuf_addstr(&summary, "\n\n");
1029
0
  }
1030
0
  strbuf_addbuf(&summary, &cmd_stdout);
1031
0
  strbuf_release(&cmd_stdout);
1032
1033
0
  if (s->display_comment_prefix) {
1034
0
    size_t len;
1035
0
    summary_content = strbuf_detach(&summary, &len);
1036
0
    strbuf_add_commented_lines(&summary, summary_content, len, comment_line_str);
1037
0
    free(summary_content);
1038
0
  }
1039
1040
0
  fputs(summary.buf, s->fp);
1041
0
  strbuf_release(&summary);
1042
0
}
1043
1044
static void wt_longstatus_print_other(struct wt_status *s,
1045
              struct string_list *l,
1046
              const char *what,
1047
              const char *how)
1048
82
{
1049
82
  int i;
1050
82
  struct strbuf buf = STRBUF_INIT;
1051
82
  static struct string_list output = STRING_LIST_INIT_DUP;
1052
82
  struct column_options copts;
1053
1054
82
  if (!l->nr)
1055
0
    return;
1056
1057
82
  wt_longstatus_print_other_header(s, what, how);
1058
1059
3.44k
  for (i = 0; i < l->nr; i++) {
1060
3.36k
    struct string_list_item *it;
1061
3.36k
    const char *path;
1062
3.36k
    it = &(l->items[i]);
1063
3.36k
    path = quote_path(it->string, s->prefix, &buf, 0);
1064
3.36k
    if (column_active(s->colopts)) {
1065
0
      string_list_append(&output, path);
1066
0
      continue;
1067
0
    }
1068
3.36k
    status_printf(s, color(WT_STATUS_HEADER, s), "\t");
1069
3.36k
    status_printf_more(s, color(WT_STATUS_UNTRACKED, s),
1070
3.36k
           "%s\n", path);
1071
3.36k
  }
1072
1073
82
  strbuf_release(&buf);
1074
82
  if (!column_active(s->colopts))
1075
82
    goto conclude;
1076
1077
0
  strbuf_addf(&buf, "%s%s\t%s",
1078
0
        color(WT_STATUS_HEADER, s),
1079
0
        s->display_comment_prefix ? "#" : "",
1080
0
        color(WT_STATUS_UNTRACKED, s));
1081
0
  memset(&copts, 0, sizeof(copts));
1082
0
  copts.padding = 1;
1083
0
  copts.indent = buf.buf;
1084
0
  if (want_color(s->use_color))
1085
0
    copts.nl = GIT_COLOR_RESET "\n";
1086
0
  print_columns(&output, s->colopts, &copts);
1087
0
  string_list_clear(&output, 0);
1088
0
  strbuf_release(&buf);
1089
82
conclude:
1090
82
  status_printf_ln(s, GIT_COLOR_NORMAL, "%s", "");
1091
82
}
1092
1093
size_t wt_status_locate_end(const char *s, size_t len)
1094
0
{
1095
0
  const char *p;
1096
0
  struct strbuf pattern = STRBUF_INIT;
1097
1098
0
  strbuf_addf(&pattern, "\n%s %s", comment_line_str, cut_line);
1099
0
  if (starts_with(s, pattern.buf + 1))
1100
0
    len = 0;
1101
0
  else if ((p = strstr(s, pattern.buf))) {
1102
0
    size_t newlen = p - s + 1;
1103
0
    if (newlen < len)
1104
0
      len = newlen;
1105
0
  }
1106
0
  strbuf_release(&pattern);
1107
0
  return len;
1108
0
}
1109
1110
void wt_status_append_cut_line(struct strbuf *buf)
1111
0
{
1112
0
  const char *explanation = _("Do not modify or remove the line above.\nEverything below it will be ignored.");
1113
1114
0
  strbuf_commented_addf(buf, comment_line_str, "%s", cut_line);
1115
0
  strbuf_add_commented_lines(buf, explanation, strlen(explanation), comment_line_str);
1116
0
}
1117
1118
void wt_status_add_cut_line(struct wt_status *s)
1119
0
{
1120
0
  struct strbuf buf = STRBUF_INIT;
1121
1122
0
  if (s->added_cut_line)
1123
0
    return;
1124
0
  s->added_cut_line = 1;
1125
0
  wt_status_append_cut_line(&buf);
1126
0
  fputs(buf.buf, s->fp);
1127
0
  strbuf_release(&buf);
1128
0
}
1129
1130
static void wt_longstatus_print_verbose(struct wt_status *s)
1131
0
{
1132
0
  struct rev_info rev;
1133
0
  struct setup_revision_opt opt;
1134
0
  int dirty_submodules;
1135
0
  const char *c = color(WT_STATUS_HEADER, s);
1136
1137
0
  repo_init_revisions(s->repo, &rev, NULL);
1138
0
  rev.diffopt.flags.allow_textconv = 1;
1139
0
  rev.diffopt.ita_invisible_in_index = 1;
1140
1141
0
  memset(&opt, 0, sizeof(opt));
1142
0
  opt.def = s->is_initial ? empty_tree_oid_hex(the_repository->hash_algo) : s->reference;
1143
0
  setup_revisions(0, NULL, &rev, &opt);
1144
1145
0
  rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
1146
0
  rev.diffopt.detect_rename = s->detect_rename >= 0 ? s->detect_rename : rev.diffopt.detect_rename;
1147
0
  rev.diffopt.rename_limit = s->rename_limit >= 0 ? s->rename_limit : rev.diffopt.rename_limit;
1148
0
  rev.diffopt.rename_score = s->rename_score >= 0 ? s->rename_score : rev.diffopt.rename_score;
1149
0
  rev.diffopt.file = s->fp;
1150
0
  rev.diffopt.close_file = 0;
1151
  /*
1152
   * If we're not going to stdout, then we definitely don't
1153
   * want color, since we are going to the commit message
1154
   * file (and even the "auto" setting won't work, since it
1155
   * will have checked isatty on stdout). But we then do want
1156
   * to insert the scissor line here to reliably remove the
1157
   * diff before committing, if we didn't already include one
1158
   * before.
1159
   */
1160
0
  if (s->fp != stdout) {
1161
0
    rev.diffopt.use_color = 0;
1162
0
    wt_status_add_cut_line(s);
1163
0
  }
1164
0
  if (s->verbose > 1 && s->committable) {
1165
    /* print_updated() printed a header, so do we */
1166
0
    if (s->fp != stdout)
1167
0
      wt_longstatus_print_trailer(s);
1168
0
    status_printf_ln(s, c, _("Changes to be committed:"));
1169
0
    rev.diffopt.a_prefix = "c/";
1170
0
    rev.diffopt.b_prefix = "i/";
1171
0
  } /* else use prefix as per user config */
1172
0
  run_diff_index(&rev, DIFF_INDEX_CACHED);
1173
0
  if (s->verbose > 1 &&
1174
0
      wt_status_check_worktree_changes(s, &dirty_submodules)) {
1175
0
    status_printf_ln(s, c,
1176
0
      "--------------------------------------------------");
1177
0
    status_printf_ln(s, c, _("Changes not staged for commit:"));
1178
0
    setup_work_tree();
1179
0
    rev.diffopt.a_prefix = "i/";
1180
0
    rev.diffopt.b_prefix = "w/";
1181
0
    run_diff_files(&rev, 0);
1182
0
  }
1183
0
  release_revisions(&rev);
1184
0
}
1185
1186
static void wt_longstatus_print_tracking(struct wt_status *s)
1187
82
{
1188
82
  struct strbuf sb = STRBUF_INIT;
1189
82
  const char *cp, *ep, *branch_name;
1190
82
  struct branch *branch;
1191
82
  uint64_t t_begin = 0;
1192
1193
82
  assert(s->branch && !s->is_initial);
1194
82
  if (!skip_prefix(s->branch, "refs/heads/", &branch_name))
1195
0
    return;
1196
82
  branch = branch_get(branch_name);
1197
1198
82
  t_begin = getnanotime();
1199
1200
82
  if (!format_tracking_info(branch, &sb, s->ahead_behind_flags,
1201
82
          !s->commit_template))
1202
82
    return;
1203
1204
0
  if (advice_enabled(ADVICE_STATUS_AHEAD_BEHIND_WARNING) &&
1205
0
      s->ahead_behind_flags == AHEAD_BEHIND_FULL) {
1206
0
    uint64_t t_delta_in_ms = (getnanotime() - t_begin) / 1000000;
1207
0
    if (t_delta_in_ms > AB_DELAY_WARNING_IN_MS) {
1208
0
      strbuf_addf(&sb, _("\n"
1209
0
             "It took %.2f seconds to compute the branch ahead/behind values.\n"
1210
0
             "You can use '--no-ahead-behind' to avoid this.\n"),
1211
0
            t_delta_in_ms / 1000.0);
1212
0
    }
1213
0
  }
1214
1215
0
  for (cp = sb.buf; (ep = strchr(cp, '\n')) != NULL; cp = ep + 1)
1216
0
    color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s),
1217
0
         "%s%s%.*s",
1218
0
         s->display_comment_prefix ? comment_line_str : "",
1219
0
         s->display_comment_prefix ? " " : "",
1220
0
         (int)(ep - cp), cp);
1221
0
  if (s->display_comment_prefix)
1222
0
    color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "%s",
1223
0
         comment_line_str);
1224
0
  else
1225
0
    fputs("\n", s->fp);
1226
0
  strbuf_release(&sb);
1227
0
}
1228
1229
static int uf_was_slow(struct wt_status *s)
1230
82
{
1231
82
  if (getenv("GIT_TEST_UF_DELAY_WARNING"))
1232
0
    s->untracked_in_ms = 3250;
1233
82
  return UF_DELAY_WARNING_IN_MS < s->untracked_in_ms;
1234
82
}
1235
1236
static void show_merge_in_progress(struct wt_status *s,
1237
           const char *color)
1238
0
{
1239
0
  if (has_unmerged(s)) {
1240
0
    status_printf_ln(s, color, _("You have unmerged paths."));
1241
0
    if (s->hints) {
1242
0
      status_printf_ln(s, color,
1243
0
           _("  (fix conflicts and run \"git commit\")"));
1244
0
      status_printf_ln(s, color,
1245
0
           _("  (use \"git merge --abort\" to abort the merge)"));
1246
0
    }
1247
0
  } else {
1248
0
    status_printf_ln(s, color,
1249
0
      _("All conflicts fixed but you are still merging."));
1250
0
    if (s->hints)
1251
0
      status_printf_ln(s, color,
1252
0
        _("  (use \"git commit\" to conclude merge)"));
1253
0
  }
1254
0
  wt_longstatus_print_trailer(s);
1255
0
}
1256
1257
static void show_am_in_progress(struct wt_status *s,
1258
        const char *color)
1259
0
{
1260
0
  int am_empty_patch;
1261
1262
0
  status_printf_ln(s, color,
1263
0
    _("You are in the middle of an am session."));
1264
0
  if (s->state.am_empty_patch)
1265
0
    status_printf_ln(s, color,
1266
0
      _("The current patch is empty."));
1267
0
  if (s->hints) {
1268
0
    am_empty_patch = s->state.am_empty_patch;
1269
0
    if (!am_empty_patch)
1270
0
      status_printf_ln(s, color,
1271
0
        _("  (fix conflicts and then run \"git am --continue\")"));
1272
0
    status_printf_ln(s, color,
1273
0
      _("  (use \"git am --skip\" to skip this patch)"));
1274
0
    if (am_empty_patch)
1275
0
      status_printf_ln(s, color,
1276
0
        _("  (use \"git am --allow-empty\" to record this patch as an empty commit)"));
1277
0
    status_printf_ln(s, color,
1278
0
      _("  (use \"git am --abort\" to restore the original branch)"));
1279
0
  }
1280
0
  wt_longstatus_print_trailer(s);
1281
0
}
1282
1283
static char *read_line_from_git_path(const char *filename)
1284
0
{
1285
0
  struct strbuf buf = STRBUF_INIT;
1286
0
  FILE *fp = fopen_or_warn(git_path("%s", filename), "r");
1287
1288
0
  if (!fp) {
1289
0
    strbuf_release(&buf);
1290
0
    return NULL;
1291
0
  }
1292
0
  strbuf_getline_lf(&buf, fp);
1293
0
  if (!fclose(fp)) {
1294
0
    return strbuf_detach(&buf, NULL);
1295
0
  } else {
1296
0
    strbuf_release(&buf);
1297
0
    return NULL;
1298
0
  }
1299
0
}
1300
1301
static int split_commit_in_progress(struct wt_status *s)
1302
0
{
1303
0
  int split_in_progress = 0;
1304
0
  struct object_id head_oid, orig_head_oid;
1305
0
  char *rebase_amend, *rebase_orig_head;
1306
0
  int head_flags, orig_head_flags;
1307
1308
0
  if ((!s->amend && !s->nowarn && !s->workdir_dirty) ||
1309
0
      !s->branch || strcmp(s->branch, "HEAD"))
1310
0
    return 0;
1311
1312
0
  if (refs_read_ref_full(get_main_ref_store(the_repository), "HEAD", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
1313
0
             &head_oid, &head_flags) ||
1314
0
      refs_read_ref_full(get_main_ref_store(the_repository), "ORIG_HEAD", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
1315
0
             &orig_head_oid, &orig_head_flags))
1316
0
    return 0;
1317
0
  if (head_flags & REF_ISSYMREF || orig_head_flags & REF_ISSYMREF)
1318
0
    return 0;
1319
1320
0
  rebase_amend = read_line_from_git_path("rebase-merge/amend");
1321
0
  rebase_orig_head = read_line_from_git_path("rebase-merge/orig-head");
1322
1323
0
  if (!rebase_amend || !rebase_orig_head)
1324
0
    ; /* fall through, no split in progress */
1325
0
  else if (!strcmp(rebase_amend, rebase_orig_head))
1326
0
    split_in_progress = !!strcmp(oid_to_hex(&head_oid), rebase_amend);
1327
0
  else if (strcmp(oid_to_hex(&orig_head_oid), rebase_orig_head))
1328
0
    split_in_progress = 1;
1329
1330
0
  free(rebase_amend);
1331
0
  free(rebase_orig_head);
1332
1333
0
  return split_in_progress;
1334
0
}
1335
1336
/*
1337
 * Turn
1338
 * "pick d6a2f0303e897ec257dd0e0a39a5ccb709bc2047 some message"
1339
 * into
1340
 * "pick d6a2f03 some message"
1341
 *
1342
 * The function assumes that the line does not contain useless spaces
1343
 * before or after the command.
1344
 */
1345
static void abbrev_oid_in_line(struct strbuf *line)
1346
0
{
1347
0
  struct strbuf **split;
1348
0
  int i;
1349
1350
0
  if (starts_with(line->buf, "exec ") ||
1351
0
      starts_with(line->buf, "x ") ||
1352
0
      starts_with(line->buf, "label ") ||
1353
0
      starts_with(line->buf, "l "))
1354
0
    return;
1355
1356
0
  split = strbuf_split_max(line, ' ', 3);
1357
0
  if (split[0] && split[1]) {
1358
0
    struct object_id oid;
1359
1360
    /*
1361
     * strbuf_split_max left a space. Trim it and re-add
1362
     * it after abbreviation.
1363
     */
1364
0
    strbuf_trim(split[1]);
1365
0
    if (!repo_get_oid(the_repository, split[1]->buf, &oid)) {
1366
0
      strbuf_reset(split[1]);
1367
0
      strbuf_add_unique_abbrev(split[1], &oid,
1368
0
             DEFAULT_ABBREV);
1369
0
      strbuf_addch(split[1], ' ');
1370
0
      strbuf_reset(line);
1371
0
      for (i = 0; split[i]; i++)
1372
0
        strbuf_addbuf(line, split[i]);
1373
0
    }
1374
0
  }
1375
0
  strbuf_list_free(split);
1376
0
}
1377
1378
static int read_rebase_todolist(const char *fname, struct string_list *lines)
1379
0
{
1380
0
  struct strbuf line = STRBUF_INIT;
1381
0
  FILE *f = fopen(git_path("%s", fname), "r");
1382
1383
0
  if (!f) {
1384
0
    if (errno == ENOENT)
1385
0
      return -1;
1386
0
    die_errno("Could not open file %s for reading",
1387
0
        git_path("%s", fname));
1388
0
  }
1389
0
  while (!strbuf_getline_lf(&line, f)) {
1390
0
    if (starts_with(line.buf, comment_line_str))
1391
0
      continue;
1392
0
    strbuf_trim(&line);
1393
0
    if (!line.len)
1394
0
      continue;
1395
0
    abbrev_oid_in_line(&line);
1396
0
    string_list_append(lines, line.buf);
1397
0
  }
1398
0
  fclose(f);
1399
0
  strbuf_release(&line);
1400
0
  return 0;
1401
0
}
1402
1403
static void show_rebase_information(struct wt_status *s,
1404
            const char *color)
1405
0
{
1406
0
  if (s->state.rebase_interactive_in_progress) {
1407
0
    int i;
1408
0
    int nr_lines_to_show = 2;
1409
1410
0
    struct string_list have_done = STRING_LIST_INIT_DUP;
1411
0
    struct string_list yet_to_do = STRING_LIST_INIT_DUP;
1412
1413
0
    read_rebase_todolist("rebase-merge/done", &have_done);
1414
0
    if (read_rebase_todolist("rebase-merge/git-rebase-todo",
1415
0
           &yet_to_do))
1416
0
      status_printf_ln(s, color,
1417
0
        _("git-rebase-todo is missing."));
1418
0
    if (have_done.nr == 0)
1419
0
      status_printf_ln(s, color, _("No commands done."));
1420
0
    else {
1421
0
      status_printf_ln(s, color,
1422
0
        Q_("Last command done (%"PRIuMAX" command done):",
1423
0
          "Last commands done (%"PRIuMAX" commands done):",
1424
0
          have_done.nr),
1425
0
        (uintmax_t)have_done.nr);
1426
0
      for (i = (have_done.nr > nr_lines_to_show)
1427
0
        ? have_done.nr - nr_lines_to_show : 0;
1428
0
        i < have_done.nr;
1429
0
        i++)
1430
0
        status_printf_ln(s, color, "   %s", have_done.items[i].string);
1431
0
      if (have_done.nr > nr_lines_to_show && s->hints)
1432
0
        status_printf_ln(s, color,
1433
0
          _("  (see more in file %s)"), git_path("rebase-merge/done"));
1434
0
    }
1435
1436
0
    if (yet_to_do.nr == 0)
1437
0
      status_printf_ln(s, color,
1438
0
           _("No commands remaining."));
1439
0
    else {
1440
0
      status_printf_ln(s, color,
1441
0
        Q_("Next command to do (%"PRIuMAX" remaining command):",
1442
0
          "Next commands to do (%"PRIuMAX" remaining commands):",
1443
0
          yet_to_do.nr),
1444
0
        (uintmax_t)yet_to_do.nr);
1445
0
      for (i = 0; i < nr_lines_to_show && i < yet_to_do.nr; i++)
1446
0
        status_printf_ln(s, color, "   %s", yet_to_do.items[i].string);
1447
0
      if (s->hints)
1448
0
        status_printf_ln(s, color,
1449
0
          _("  (use \"git rebase --edit-todo\" to view and edit)"));
1450
0
    }
1451
0
    string_list_clear(&yet_to_do, 0);
1452
0
    string_list_clear(&have_done, 0);
1453
0
  }
1454
0
}
1455
1456
static void print_rebase_state(struct wt_status *s,
1457
             const char *color)
1458
0
{
1459
0
  if (s->state.branch)
1460
0
    status_printf_ln(s, color,
1461
0
         _("You are currently rebasing branch '%s' on '%s'."),
1462
0
         s->state.branch,
1463
0
         s->state.onto);
1464
0
  else
1465
0
    status_printf_ln(s, color,
1466
0
         _("You are currently rebasing."));
1467
0
}
1468
1469
static void show_rebase_in_progress(struct wt_status *s,
1470
            const char *color)
1471
0
{
1472
0
  struct stat st;
1473
1474
0
  show_rebase_information(s, color);
1475
0
  if (has_unmerged(s)) {
1476
0
    print_rebase_state(s, color);
1477
0
    if (s->hints) {
1478
0
      status_printf_ln(s, color,
1479
0
        _("  (fix conflicts and then run \"git rebase --continue\")"));
1480
0
      status_printf_ln(s, color,
1481
0
        _("  (use \"git rebase --skip\" to skip this patch)"));
1482
0
      status_printf_ln(s, color,
1483
0
        _("  (use \"git rebase --abort\" to check out the original branch)"));
1484
0
    }
1485
0
  } else if (s->state.rebase_in_progress ||
1486
0
       !stat(git_path_merge_msg(s->repo), &st)) {
1487
0
    print_rebase_state(s, color);
1488
0
    if (s->hints)
1489
0
      status_printf_ln(s, color,
1490
0
        _("  (all conflicts fixed: run \"git rebase --continue\")"));
1491
0
  } else if (split_commit_in_progress(s)) {
1492
0
    if (s->state.branch)
1493
0
      status_printf_ln(s, color,
1494
0
           _("You are currently splitting a commit while rebasing branch '%s' on '%s'."),
1495
0
           s->state.branch,
1496
0
           s->state.onto);
1497
0
    else
1498
0
      status_printf_ln(s, color,
1499
0
           _("You are currently splitting a commit during a rebase."));
1500
0
    if (s->hints)
1501
0
      status_printf_ln(s, color,
1502
0
        _("  (Once your working directory is clean, run \"git rebase --continue\")"));
1503
0
  } else {
1504
0
    if (s->state.branch)
1505
0
      status_printf_ln(s, color,
1506
0
           _("You are currently editing a commit while rebasing branch '%s' on '%s'."),
1507
0
           s->state.branch,
1508
0
           s->state.onto);
1509
0
    else
1510
0
      status_printf_ln(s, color,
1511
0
           _("You are currently editing a commit during a rebase."));
1512
0
    if (s->hints && !s->amend) {
1513
0
      status_printf_ln(s, color,
1514
0
        _("  (use \"git commit --amend\" to amend the current commit)"));
1515
0
      status_printf_ln(s, color,
1516
0
        _("  (use \"git rebase --continue\" once you are satisfied with your changes)"));
1517
0
    }
1518
0
  }
1519
0
  wt_longstatus_print_trailer(s);
1520
0
}
1521
1522
static void show_cherry_pick_in_progress(struct wt_status *s,
1523
           const char *color)
1524
0
{
1525
0
  if (is_null_oid(&s->state.cherry_pick_head_oid))
1526
0
    status_printf_ln(s, color,
1527
0
      _("Cherry-pick currently in progress."));
1528
0
  else
1529
0
    status_printf_ln(s, color,
1530
0
      _("You are currently cherry-picking commit %s."),
1531
0
      repo_find_unique_abbrev(the_repository, &s->state.cherry_pick_head_oid,
1532
0
            DEFAULT_ABBREV));
1533
1534
0
  if (s->hints) {
1535
0
    if (has_unmerged(s))
1536
0
      status_printf_ln(s, color,
1537
0
        _("  (fix conflicts and run \"git cherry-pick --continue\")"));
1538
0
    else if (is_null_oid(&s->state.cherry_pick_head_oid))
1539
0
      status_printf_ln(s, color,
1540
0
        _("  (run \"git cherry-pick --continue\" to continue)"));
1541
0
    else
1542
0
      status_printf_ln(s, color,
1543
0
        _("  (all conflicts fixed: run \"git cherry-pick --continue\")"));
1544
0
    status_printf_ln(s, color,
1545
0
      _("  (use \"git cherry-pick --skip\" to skip this patch)"));
1546
0
    status_printf_ln(s, color,
1547
0
      _("  (use \"git cherry-pick --abort\" to cancel the cherry-pick operation)"));
1548
0
  }
1549
0
  wt_longstatus_print_trailer(s);
1550
0
}
1551
1552
static void show_revert_in_progress(struct wt_status *s,
1553
            const char *color)
1554
0
{
1555
0
  if (is_null_oid(&s->state.revert_head_oid))
1556
0
    status_printf_ln(s, color,
1557
0
      _("Revert currently in progress."));
1558
0
  else
1559
0
    status_printf_ln(s, color,
1560
0
      _("You are currently reverting commit %s."),
1561
0
      repo_find_unique_abbrev(the_repository, &s->state.revert_head_oid,
1562
0
            DEFAULT_ABBREV));
1563
0
  if (s->hints) {
1564
0
    if (has_unmerged(s))
1565
0
      status_printf_ln(s, color,
1566
0
        _("  (fix conflicts and run \"git revert --continue\")"));
1567
0
    else if (is_null_oid(&s->state.revert_head_oid))
1568
0
      status_printf_ln(s, color,
1569
0
        _("  (run \"git revert --continue\" to continue)"));
1570
0
    else
1571
0
      status_printf_ln(s, color,
1572
0
        _("  (all conflicts fixed: run \"git revert --continue\")"));
1573
0
    status_printf_ln(s, color,
1574
0
      _("  (use \"git revert --skip\" to skip this patch)"));
1575
0
    status_printf_ln(s, color,
1576
0
      _("  (use \"git revert --abort\" to cancel the revert operation)"));
1577
0
  }
1578
0
  wt_longstatus_print_trailer(s);
1579
0
}
1580
1581
static void show_bisect_in_progress(struct wt_status *s,
1582
            const char *color)
1583
0
{
1584
0
  if (s->state.bisecting_from)
1585
0
    status_printf_ln(s, color,
1586
0
         _("You are currently bisecting, started from branch '%s'."),
1587
0
         s->state.bisecting_from);
1588
0
  else
1589
0
    status_printf_ln(s, color,
1590
0
         _("You are currently bisecting."));
1591
0
  if (s->hints)
1592
0
    status_printf_ln(s, color,
1593
0
      _("  (use \"git bisect reset\" to get back to the original branch)"));
1594
0
  wt_longstatus_print_trailer(s);
1595
0
}
1596
1597
static void show_sparse_checkout_in_use(struct wt_status *s,
1598
          const char *color)
1599
0
{
1600
0
  if (s->state.sparse_checkout_percentage == SPARSE_CHECKOUT_DISABLED)
1601
0
    return;
1602
1603
0
  if (s->state.sparse_checkout_percentage == SPARSE_CHECKOUT_SPARSE_INDEX)
1604
0
    status_printf_ln(s, color, _("You are in a sparse checkout."));
1605
0
  else
1606
0
    status_printf_ln(s, color,
1607
0
        _("You are in a sparse checkout with %d%% of tracked files present."),
1608
0
        s->state.sparse_checkout_percentage);
1609
0
  wt_longstatus_print_trailer(s);
1610
0
}
1611
1612
/*
1613
 * Extract branch information from rebase/bisect
1614
 */
1615
static char *get_branch(const struct worktree *wt, const char *path)
1616
0
{
1617
0
  struct strbuf sb = STRBUF_INIT;
1618
0
  struct object_id oid;
1619
0
  const char *branch_name;
1620
1621
0
  if (strbuf_read_file(&sb, worktree_git_path(the_repository, wt, "%s", path), 0) <= 0)
1622
0
    goto got_nothing;
1623
1624
0
  while (sb.len && sb.buf[sb.len - 1] == '\n')
1625
0
    strbuf_setlen(&sb, sb.len - 1);
1626
0
  if (!sb.len)
1627
0
    goto got_nothing;
1628
0
  if (skip_prefix(sb.buf, "refs/heads/", &branch_name))
1629
0
    strbuf_remove(&sb, 0, branch_name - sb.buf);
1630
0
  else if (starts_with(sb.buf, "refs/"))
1631
0
    ;
1632
0
  else if (!get_oid_hex(sb.buf, &oid)) {
1633
0
    strbuf_reset(&sb);
1634
0
    strbuf_add_unique_abbrev(&sb, &oid, DEFAULT_ABBREV);
1635
0
  } else if (!strcmp(sb.buf, "detached HEAD")) /* rebase */
1636
0
    goto got_nothing;
1637
0
  else      /* bisect */
1638
0
    ;
1639
0
  return strbuf_detach(&sb, NULL);
1640
1641
0
got_nothing:
1642
0
  strbuf_release(&sb);
1643
0
  return NULL;
1644
0
}
1645
1646
struct grab_1st_switch_cbdata {
1647
  struct strbuf buf;
1648
  struct object_id noid;
1649
};
1650
1651
static int grab_1st_switch(struct object_id *ooid UNUSED,
1652
         struct object_id *noid,
1653
         const char *email UNUSED,
1654
         timestamp_t timestamp UNUSED, int tz UNUSED,
1655
         const char *message, void *cb_data)
1656
0
{
1657
0
  struct grab_1st_switch_cbdata *cb = cb_data;
1658
0
  const char *target = NULL, *end;
1659
1660
0
  if (!skip_prefix(message, "checkout: moving from ", &message))
1661
0
    return 0;
1662
0
  target = strstr(message, " to ");
1663
0
  if (!target)
1664
0
    return 0;
1665
0
  target += strlen(" to ");
1666
0
  strbuf_reset(&cb->buf);
1667
0
  oidcpy(&cb->noid, noid);
1668
0
  end = strchrnul(target, '\n');
1669
0
  strbuf_add(&cb->buf, target, end - target);
1670
0
  if (!strcmp(cb->buf.buf, "HEAD")) {
1671
    /* HEAD is relative. Resolve it to the right reflog entry. */
1672
0
    strbuf_reset(&cb->buf);
1673
0
    strbuf_add_unique_abbrev(&cb->buf, noid, DEFAULT_ABBREV);
1674
0
  }
1675
0
  return 1;
1676
0
}
1677
1678
static void wt_status_get_detached_from(struct repository *r,
1679
          struct wt_status_state *state)
1680
0
{
1681
0
  struct grab_1st_switch_cbdata cb;
1682
0
  struct commit *commit;
1683
0
  struct object_id oid;
1684
0
  char *ref = NULL;
1685
1686
0
  strbuf_init(&cb.buf, 0);
1687
0
  if (refs_for_each_reflog_ent_reverse(get_main_ref_store(the_repository), "HEAD", grab_1st_switch, &cb) <= 0) {
1688
0
    strbuf_release(&cb.buf);
1689
0
    return;
1690
0
  }
1691
1692
0
  if (repo_dwim_ref(r, cb.buf.buf, cb.buf.len, &oid, &ref,
1693
0
        1) == 1 &&
1694
      /* oid is a commit? match without further lookup */
1695
0
      (oideq(&cb.noid, &oid) ||
1696
       /* perhaps oid is a tag, try to dereference to a commit */
1697
0
       ((commit = lookup_commit_reference_gently(r, &oid, 1)) != NULL &&
1698
0
        oideq(&cb.noid, &commit->object.oid)))) {
1699
0
    const char *from = ref;
1700
0
    if (!skip_prefix(from, "refs/tags/", &from))
1701
0
      skip_prefix(from, "refs/remotes/", &from);
1702
0
    state->detached_from = xstrdup(from);
1703
0
  } else
1704
0
    state->detached_from =
1705
0
      xstrdup(repo_find_unique_abbrev(r, &cb.noid, DEFAULT_ABBREV));
1706
0
  oidcpy(&state->detached_oid, &cb.noid);
1707
0
  state->detached_at = !repo_get_oid(r, "HEAD", &oid) &&
1708
0
           oideq(&oid, &state->detached_oid);
1709
1710
0
  free(ref);
1711
0
  strbuf_release(&cb.buf);
1712
0
}
1713
1714
int wt_status_check_rebase(const struct worktree *wt,
1715
         struct wt_status_state *state)
1716
82
{
1717
82
  struct stat st;
1718
1719
82
  if (!stat(worktree_git_path(the_repository, wt, "rebase-apply"), &st)) {
1720
0
    if (!stat(worktree_git_path(the_repository, wt, "rebase-apply/applying"), &st)) {
1721
0
      state->am_in_progress = 1;
1722
0
      if (!stat(worktree_git_path(the_repository, wt, "rebase-apply/patch"), &st) && !st.st_size)
1723
0
        state->am_empty_patch = 1;
1724
0
    } else {
1725
0
      state->rebase_in_progress = 1;
1726
0
      state->branch = get_branch(wt, "rebase-apply/head-name");
1727
0
      state->onto = get_branch(wt, "rebase-apply/onto");
1728
0
    }
1729
82
  } else if (!stat(worktree_git_path(the_repository, wt, "rebase-merge"), &st)) {
1730
0
    if (!stat(worktree_git_path(the_repository, wt, "rebase-merge/interactive"), &st))
1731
0
      state->rebase_interactive_in_progress = 1;
1732
0
    else
1733
0
      state->rebase_in_progress = 1;
1734
0
    state->branch = get_branch(wt, "rebase-merge/head-name");
1735
0
    state->onto = get_branch(wt, "rebase-merge/onto");
1736
0
  } else
1737
82
    return 0;
1738
0
  return 1;
1739
82
}
1740
1741
int wt_status_check_bisect(const struct worktree *wt,
1742
         struct wt_status_state *state)
1743
82
{
1744
82
  struct stat st;
1745
1746
82
  if (!stat(worktree_git_path(the_repository, wt, "BISECT_LOG"), &st)) {
1747
0
    state->bisect_in_progress = 1;
1748
0
    state->bisecting_from = get_branch(wt, "BISECT_START");
1749
0
    return 1;
1750
0
  }
1751
82
  return 0;
1752
82
}
1753
1754
static void wt_status_check_sparse_checkout(struct repository *r,
1755
              struct wt_status_state *state)
1756
82
{
1757
82
  int skip_worktree = 0;
1758
82
  int i;
1759
1760
82
  if (!core_apply_sparse_checkout || r->index->cache_nr == 0) {
1761
    /*
1762
     * Don't compute percentage of checked out files if we
1763
     * aren't in a sparse checkout or would get division by 0.
1764
     */
1765
82
    state->sparse_checkout_percentage = SPARSE_CHECKOUT_DISABLED;
1766
82
    return;
1767
82
  }
1768
1769
0
  if (r->index->sparse_index) {
1770
0
    state->sparse_checkout_percentage = SPARSE_CHECKOUT_SPARSE_INDEX;
1771
0
    return;
1772
0
  }
1773
1774
0
  for (i = 0; i < r->index->cache_nr; i++) {
1775
0
    struct cache_entry *ce = r->index->cache[i];
1776
0
    if (ce_skip_worktree(ce))
1777
0
      skip_worktree++;
1778
0
  }
1779
1780
0
  state->sparse_checkout_percentage =
1781
0
    100 - (100 * skip_worktree)/r->index->cache_nr;
1782
0
}
1783
1784
void wt_status_get_state(struct repository *r,
1785
       struct wt_status_state *state,
1786
       int get_detached_from)
1787
82
{
1788
82
  struct stat st;
1789
82
  struct object_id oid;
1790
82
  enum replay_action action;
1791
1792
82
  if (!stat(git_path_merge_head(r), &st)) {
1793
0
    wt_status_check_rebase(NULL, state);
1794
0
    state->merge_in_progress = 1;
1795
82
  } else if (wt_status_check_rebase(NULL, state)) {
1796
0
    ;   /* all set */
1797
82
  } else if (refs_ref_exists(get_main_ref_store(r), "CHERRY_PICK_HEAD") &&
1798
82
       !repo_get_oid(r, "CHERRY_PICK_HEAD", &oid)) {
1799
0
    state->cherry_pick_in_progress = 1;
1800
0
    oidcpy(&state->cherry_pick_head_oid, &oid);
1801
0
  }
1802
82
  wt_status_check_bisect(NULL, state);
1803
82
  if (refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD") &&
1804
82
      !repo_get_oid(r, "REVERT_HEAD", &oid)) {
1805
0
    state->revert_in_progress = 1;
1806
0
    oidcpy(&state->revert_head_oid, &oid);
1807
0
  }
1808
82
  if (!sequencer_get_last_command(r, &action)) {
1809
0
    if (action == REPLAY_PICK && !state->cherry_pick_in_progress) {
1810
0
      state->cherry_pick_in_progress = 1;
1811
0
      oidcpy(&state->cherry_pick_head_oid, null_oid());
1812
0
    } else if (action == REPLAY_REVERT && !state->revert_in_progress) {
1813
0
      state->revert_in_progress = 1;
1814
0
      oidcpy(&state->revert_head_oid, null_oid());
1815
0
    }
1816
0
  }
1817
82
  if (get_detached_from)
1818
0
    wt_status_get_detached_from(r, state);
1819
82
  wt_status_check_sparse_checkout(r, state);
1820
82
}
1821
1822
static void wt_longstatus_print_state(struct wt_status *s)
1823
82
{
1824
82
  const char *state_color = color(WT_STATUS_HEADER, s);
1825
82
  struct wt_status_state *state = &s->state;
1826
1827
82
  if (state->merge_in_progress) {
1828
0
    if (state->rebase_interactive_in_progress) {
1829
0
      show_rebase_information(s, state_color);
1830
0
      fputs("\n", s->fp);
1831
0
    }
1832
0
    show_merge_in_progress(s, state_color);
1833
82
  } else if (state->am_in_progress)
1834
0
    show_am_in_progress(s, state_color);
1835
82
  else if (state->rebase_in_progress || state->rebase_interactive_in_progress)
1836
0
    show_rebase_in_progress(s, state_color);
1837
82
  else if (state->cherry_pick_in_progress)
1838
0
    show_cherry_pick_in_progress(s, state_color);
1839
82
  else if (state->revert_in_progress)
1840
0
    show_revert_in_progress(s, state_color);
1841
82
  if (state->bisect_in_progress)
1842
0
    show_bisect_in_progress(s, state_color);
1843
1844
82
  if (state->sparse_checkout_percentage != SPARSE_CHECKOUT_DISABLED)
1845
0
    show_sparse_checkout_in_use(s, state_color);
1846
82
}
1847
1848
static void wt_longstatus_print(struct wt_status *s)
1849
82
{
1850
82
  const char *branch_color = color(WT_STATUS_ONBRANCH, s);
1851
82
  const char *branch_status_color = color(WT_STATUS_HEADER, s);
1852
82
  enum fsmonitor_mode fsm_mode = fsm_settings__get_mode(s->repo);
1853
1854
82
  if (s->branch) {
1855
82
    const char *on_what = _("On branch ");
1856
82
    const char *branch_name = s->branch;
1857
82
    if (!strcmp(branch_name, "HEAD")) {
1858
0
      branch_status_color = color(WT_STATUS_NOBRANCH, s);
1859
0
      if (s->state.rebase_in_progress ||
1860
0
          s->state.rebase_interactive_in_progress) {
1861
0
        if (s->state.rebase_interactive_in_progress)
1862
0
          on_what = _("interactive rebase in progress; onto ");
1863
0
        else
1864
0
          on_what = _("rebase in progress; onto ");
1865
0
        branch_name = s->state.onto;
1866
0
      } else if (s->state.detached_from) {
1867
0
        branch_name = s->state.detached_from;
1868
0
        if (s->state.detached_at)
1869
0
          on_what = _("HEAD detached at ");
1870
0
        else
1871
0
          on_what = _("HEAD detached from ");
1872
0
      } else {
1873
0
        branch_name = "";
1874
0
        on_what = _("Not currently on any branch.");
1875
0
      }
1876
0
    } else
1877
82
      skip_prefix(branch_name, "refs/heads/", &branch_name);
1878
82
    status_printf(s, color(WT_STATUS_HEADER, s), "%s", "");
1879
82
    status_printf_more(s, branch_status_color, "%s", on_what);
1880
82
    status_printf_more(s, branch_color, "%s\n", branch_name);
1881
82
    if (!s->is_initial)
1882
82
      wt_longstatus_print_tracking(s);
1883
82
  }
1884
1885
82
  wt_longstatus_print_state(s);
1886
1887
82
  if (s->is_initial) {
1888
0
    status_printf_ln(s, color(WT_STATUS_HEADER, s), "%s", "");
1889
0
    status_printf_ln(s, color(WT_STATUS_HEADER, s),
1890
0
         s->commit_template
1891
0
         ? _("Initial commit")
1892
0
         : _("No commits yet"));
1893
0
    status_printf_ln(s, color(WT_STATUS_HEADER, s), "%s", "");
1894
0
  }
1895
1896
82
  wt_longstatus_print_updated(s);
1897
82
  wt_longstatus_print_unmerged(s);
1898
82
  wt_longstatus_print_changed(s);
1899
82
  if (s->submodule_summary &&
1900
82
      (!s->ignore_submodule_arg ||
1901
0
       strcmp(s->ignore_submodule_arg, "all"))) {
1902
0
    wt_longstatus_print_submodule_summary(s, 0);  /* staged */
1903
0
    wt_longstatus_print_submodule_summary(s, 1);  /* unstaged */
1904
0
  }
1905
82
  if (s->show_untracked_files) {
1906
82
    wt_longstatus_print_other(s, &s->untracked, _("Untracked files"), "add");
1907
82
    if (s->show_ignored_mode)
1908
0
      wt_longstatus_print_other(s, &s->ignored, _("Ignored files"), "add -f");
1909
82
    if (advice_enabled(ADVICE_STATUS_U_OPTION) && uf_was_slow(s)) {
1910
0
      status_printf_ln(s, GIT_COLOR_NORMAL, "%s", "");
1911
0
      if (fsm_mode > FSMONITOR_MODE_DISABLED) {
1912
0
        status_printf_ln(s, GIT_COLOR_NORMAL,
1913
0
            _("It took %.2f seconds to enumerate untracked files,\n"
1914
0
            "but the results were cached, and subsequent runs may be faster."),
1915
0
            s->untracked_in_ms / 1000.0);
1916
0
      } else {
1917
0
        status_printf_ln(s, GIT_COLOR_NORMAL,
1918
0
            _("It took %.2f seconds to enumerate untracked files."),
1919
0
            s->untracked_in_ms / 1000.0);
1920
0
      }
1921
0
      status_printf_ln(s, GIT_COLOR_NORMAL,
1922
0
          _("See 'git help status' for information on how to improve this."));
1923
0
      status_printf_ln(s, GIT_COLOR_NORMAL, "%s", "");
1924
0
    }
1925
82
  } else if (s->committable)
1926
0
    status_printf_ln(s, GIT_COLOR_NORMAL, _("Untracked files not listed%s"),
1927
0
      s->hints
1928
0
      ? _(" (use -u option to show untracked files)") : "");
1929
1930
82
  if (s->verbose)
1931
0
    wt_longstatus_print_verbose(s);
1932
82
  if (!s->committable) {
1933
82
    if (s->amend)
1934
0
      status_printf_ln(s, GIT_COLOR_NORMAL, _("No changes"));
1935
82
    else if (s->nowarn)
1936
0
      ; /* nothing */
1937
82
    else if (s->workdir_dirty) {
1938
0
      if (s->hints)
1939
0
        fprintf(s->fp, _("no changes added to commit "
1940
0
             "(use \"git add\" and/or "
1941
0
             "\"git commit -a\")\n"));
1942
0
      else
1943
0
        fprintf(s->fp, _("no changes added to "
1944
0
             "commit\n"));
1945
82
    } else if (s->untracked.nr) {
1946
82
      if (s->hints)
1947
82
        fprintf(s->fp, _("nothing added to commit but "
1948
82
             "untracked files present (use "
1949
82
             "\"git add\" to track)\n"));
1950
0
      else
1951
0
        fprintf(s->fp, _("nothing added to commit but "
1952
0
             "untracked files present\n"));
1953
82
    } else if (s->is_initial) {
1954
0
      if (s->hints)
1955
0
        fprintf(s->fp, _("nothing to commit (create/"
1956
0
             "copy files and use \"git "
1957
0
             "add\" to track)\n"));
1958
0
      else
1959
0
        fprintf(s->fp, _("nothing to commit\n"));
1960
0
    } else if (!s->show_untracked_files) {
1961
0
      if (s->hints)
1962
0
        fprintf(s->fp, _("nothing to commit (use -u to "
1963
0
             "show untracked files)\n"));
1964
0
      else
1965
0
        fprintf(s->fp, _("nothing to commit\n"));
1966
0
    } else
1967
0
      fprintf(s->fp, _("nothing to commit, working tree "
1968
0
           "clean\n"));
1969
82
  }
1970
82
  if(s->show_stash)
1971
0
    wt_longstatus_print_stash_summary(s);
1972
82
}
1973
1974
static void wt_shortstatus_unmerged(struct string_list_item *it,
1975
         struct wt_status *s)
1976
0
{
1977
0
  struct wt_status_change_data *d = it->util;
1978
0
  const char *how = "??";
1979
1980
0
  switch (d->stagemask) {
1981
0
  case 1: how = "DD"; break; /* both deleted */
1982
0
  case 2: how = "AU"; break; /* added by us */
1983
0
  case 3: how = "UD"; break; /* deleted by them */
1984
0
  case 4: how = "UA"; break; /* added by them */
1985
0
  case 5: how = "DU"; break; /* deleted by us */
1986
0
  case 6: how = "AA"; break; /* both added */
1987
0
  case 7: how = "UU"; break; /* both modified */
1988
0
  }
1989
0
  color_fprintf(s->fp, color(WT_STATUS_UNMERGED, s), "%s", how);
1990
0
  if (s->null_termination) {
1991
0
    fprintf(s->fp, " %s%c", it->string, 0);
1992
0
  } else {
1993
0
    struct strbuf onebuf = STRBUF_INIT;
1994
0
    const char *one;
1995
0
    one = quote_path(it->string, s->prefix, &onebuf, QUOTE_PATH_QUOTE_SP);
1996
0
    fprintf(s->fp, " %s\n", one);
1997
0
    strbuf_release(&onebuf);
1998
0
  }
1999
0
}
2000
2001
static void wt_shortstatus_status(struct string_list_item *it,
2002
       struct wt_status *s)
2003
0
{
2004
0
  struct wt_status_change_data *d = it->util;
2005
2006
0
  if (d->index_status)
2007
0
    color_fprintf(s->fp, color(WT_STATUS_UPDATED, s), "%c", d->index_status);
2008
0
  else
2009
0
    fputc(' ', s->fp);
2010
0
  if (d->worktree_status)
2011
0
    color_fprintf(s->fp, color(WT_STATUS_CHANGED, s), "%c", d->worktree_status);
2012
0
  else
2013
0
    fputc(' ', s->fp);
2014
0
  fputc(' ', s->fp);
2015
0
  if (s->null_termination) {
2016
0
    fprintf(s->fp, "%s%c", it->string, 0);
2017
0
    if (d->rename_source)
2018
0
      fprintf(s->fp, "%s%c", d->rename_source, 0);
2019
0
  } else {
2020
0
    struct strbuf onebuf = STRBUF_INIT;
2021
0
    const char *one;
2022
2023
0
    if (d->rename_source) {
2024
0
      one = quote_path(d->rename_source, s->prefix, &onebuf,
2025
0
           QUOTE_PATH_QUOTE_SP);
2026
0
      fprintf(s->fp, "%s -> ", one);
2027
0
      strbuf_release(&onebuf);
2028
0
    }
2029
0
    one = quote_path(it->string, s->prefix, &onebuf, QUOTE_PATH_QUOTE_SP);
2030
0
    fprintf(s->fp, "%s\n", one);
2031
0
    strbuf_release(&onebuf);
2032
0
  }
2033
0
}
2034
2035
static void wt_shortstatus_other(struct string_list_item *it,
2036
         struct wt_status *s, const char *sign)
2037
0
{
2038
0
  if (s->null_termination) {
2039
0
    fprintf(s->fp, "%s %s%c", sign, it->string, 0);
2040
0
  } else {
2041
0
    struct strbuf onebuf = STRBUF_INIT;
2042
0
    const char *one;
2043
0
    one = quote_path(it->string, s->prefix, &onebuf, QUOTE_PATH_QUOTE_SP);
2044
0
    color_fprintf(s->fp, color(WT_STATUS_UNTRACKED, s), "%s", sign);
2045
0
    fprintf(s->fp, " %s\n", one);
2046
0
    strbuf_release(&onebuf);
2047
0
  }
2048
0
}
2049
2050
static void wt_shortstatus_print_tracking(struct wt_status *s)
2051
0
{
2052
0
  struct branch *branch;
2053
0
  const char *header_color = color(WT_STATUS_HEADER, s);
2054
0
  const char *branch_color_local = color(WT_STATUS_LOCAL_BRANCH, s);
2055
0
  const char *branch_color_remote = color(WT_STATUS_REMOTE_BRANCH, s);
2056
2057
0
  const char *base;
2058
0
  char *short_base;
2059
0
  const char *branch_name;
2060
0
  int num_ours, num_theirs, sti;
2061
0
  int upstream_is_gone = 0;
2062
2063
0
  color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "## ");
2064
2065
0
  if (!s->branch)
2066
0
    return;
2067
0
  branch_name = s->branch;
2068
2069
0
#define LABEL(string) (s->no_gettext ? (string) : _(string))
2070
2071
0
  if (s->is_initial)
2072
0
    color_fprintf(s->fp, header_color, LABEL(N_("No commits yet on ")));
2073
2074
0
  if (!strcmp(s->branch, "HEAD")) {
2075
0
    color_fprintf(s->fp, color(WT_STATUS_NOBRANCH, s), "%s",
2076
0
            LABEL(N_("HEAD (no branch)")));
2077
0
    goto conclude;
2078
0
  }
2079
2080
0
  skip_prefix(branch_name, "refs/heads/", &branch_name);
2081
2082
0
  branch = branch_get(branch_name);
2083
2084
0
  color_fprintf(s->fp, branch_color_local, "%s", branch_name);
2085
2086
0
  sti = stat_tracking_info(branch, &num_ours, &num_theirs, &base,
2087
0
         0, s->ahead_behind_flags);
2088
0
  if (sti < 0) {
2089
0
    if (!base)
2090
0
      goto conclude;
2091
2092
0
    upstream_is_gone = 1;
2093
0
  }
2094
2095
0
  short_base = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
2096
0
              base, 0);
2097
0
  color_fprintf(s->fp, header_color, "...");
2098
0
  color_fprintf(s->fp, branch_color_remote, "%s", short_base);
2099
0
  free(short_base);
2100
2101
0
  if (!upstream_is_gone && !sti)
2102
0
    goto conclude;
2103
2104
0
  color_fprintf(s->fp, header_color, " [");
2105
0
  if (upstream_is_gone) {
2106
0
    color_fprintf(s->fp, header_color, LABEL(N_("gone")));
2107
0
  } else if (s->ahead_behind_flags == AHEAD_BEHIND_QUICK) {
2108
0
    color_fprintf(s->fp, header_color, LABEL(N_("different")));
2109
0
  } else if (!num_ours) {
2110
0
    color_fprintf(s->fp, header_color, LABEL(N_("behind ")));
2111
0
    color_fprintf(s->fp, branch_color_remote, "%d", num_theirs);
2112
0
  } else if (!num_theirs) {
2113
0
    color_fprintf(s->fp, header_color, LABEL(N_("ahead ")));
2114
0
    color_fprintf(s->fp, branch_color_local, "%d", num_ours);
2115
0
  } else {
2116
0
    color_fprintf(s->fp, header_color, LABEL(N_("ahead ")));
2117
0
    color_fprintf(s->fp, branch_color_local, "%d", num_ours);
2118
0
    color_fprintf(s->fp, header_color, ", %s", LABEL(N_("behind ")));
2119
0
    color_fprintf(s->fp, branch_color_remote, "%d", num_theirs);
2120
0
  }
2121
2122
0
  color_fprintf(s->fp, header_color, "]");
2123
0
 conclude:
2124
0
  fputc(s->null_termination ? '\0' : '\n', s->fp);
2125
0
}
2126
2127
static void wt_shortstatus_print(struct wt_status *s)
2128
0
{
2129
0
  struct string_list_item *it;
2130
2131
0
  if (s->show_branch)
2132
0
    wt_shortstatus_print_tracking(s);
2133
2134
0
  for_each_string_list_item(it, &s->change) {
2135
0
    struct wt_status_change_data *d = it->util;
2136
2137
0
    if (d->stagemask)
2138
0
      wt_shortstatus_unmerged(it, s);
2139
0
    else
2140
0
      wt_shortstatus_status(it, s);
2141
0
  }
2142
0
  for_each_string_list_item(it, &s->untracked)
2143
0
    wt_shortstatus_other(it, s, "??");
2144
2145
0
  for_each_string_list_item(it, &s->ignored)
2146
0
    wt_shortstatus_other(it, s, "!!");
2147
0
}
2148
2149
static void wt_porcelain_print(struct wt_status *s)
2150
0
{
2151
0
  s->use_color = 0;
2152
0
  s->relative_paths = 0;
2153
0
  s->prefix = NULL;
2154
0
  s->no_gettext = 1;
2155
0
  wt_shortstatus_print(s);
2156
0
}
2157
2158
/*
2159
 * Print branch information for porcelain v2 output.  These lines
2160
 * are printed when the '--branch' parameter is given.
2161
 *
2162
 *    # branch.oid <commit><eol>
2163
 *    # branch.head <head><eol>
2164
 *   [# branch.upstream <upstream><eol>
2165
 *   [# branch.ab +<ahead> -<behind><eol>]]
2166
 *
2167
 *      <commit> ::= the current commit hash or the literal
2168
 *                   "(initial)" to indicate an initialized repo
2169
 *                   with no commits.
2170
 *
2171
 *        <head> ::= <branch_name> the current branch name or
2172
 *                   "(detached)" literal when detached head or
2173
 *                   "(unknown)" when something is wrong.
2174
 *
2175
 *    <upstream> ::= the upstream branch name, when set.
2176
 *
2177
 *       <ahead> ::= integer ahead value or '?'.
2178
 *
2179
 *      <behind> ::= integer behind value or '?'.
2180
 *
2181
 * The end-of-line is defined by the -z flag.
2182
 *
2183
 *                 <eol> ::= NUL when -z,
2184
 *                           LF when NOT -z.
2185
 *
2186
 * When an upstream is set and present, the 'branch.ab' line will
2187
 * be printed with the ahead/behind counts for the branch and the
2188
 * upstream.  When AHEAD_BEHIND_QUICK is requested and the branches
2189
 * are different, '?' will be substituted for the actual count.
2190
 */
2191
static void wt_porcelain_v2_print_tracking(struct wt_status *s)
2192
0
{
2193
0
  struct branch *branch;
2194
0
  const char *base;
2195
0
  const char *branch_name;
2196
0
  int ab_info, nr_ahead, nr_behind;
2197
0
  char eol = s->null_termination ? '\0' : '\n';
2198
2199
0
  fprintf(s->fp, "# branch.oid %s%c",
2200
0
      (s->is_initial ? "(initial)" : oid_to_hex(&s->oid_commit)),
2201
0
      eol);
2202
2203
0
  if (!s->branch)
2204
0
    fprintf(s->fp, "# branch.head %s%c", "(unknown)", eol);
2205
0
  else {
2206
0
    if (!strcmp(s->branch, "HEAD")) {
2207
0
      fprintf(s->fp, "# branch.head %s%c", "(detached)", eol);
2208
2209
0
      if (s->state.rebase_in_progress ||
2210
0
          s->state.rebase_interactive_in_progress)
2211
0
        branch_name = s->state.onto;
2212
0
      else if (s->state.detached_from)
2213
0
        branch_name = s->state.detached_from;
2214
0
      else
2215
0
        branch_name = "";
2216
0
    } else {
2217
0
      branch_name = NULL;
2218
0
      skip_prefix(s->branch, "refs/heads/", &branch_name);
2219
2220
0
      fprintf(s->fp, "# branch.head %s%c", branch_name, eol);
2221
0
    }
2222
2223
    /* Lookup stats on the upstream tracking branch, if set. */
2224
0
    branch = branch_get(branch_name);
2225
0
    base = NULL;
2226
0
    ab_info = stat_tracking_info(branch, &nr_ahead, &nr_behind,
2227
0
               &base, 0, s->ahead_behind_flags);
2228
0
    if (base) {
2229
0
      base = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
2230
0
                  base, 0);
2231
0
      fprintf(s->fp, "# branch.upstream %s%c", base, eol);
2232
0
      free((char *)base);
2233
2234
0
      if (ab_info > 0) {
2235
        /* different */
2236
0
        if (nr_ahead || nr_behind)
2237
0
          fprintf(s->fp, "# branch.ab +%d -%d%c",
2238
0
            nr_ahead, nr_behind, eol);
2239
0
        else
2240
0
          fprintf(s->fp, "# branch.ab +? -?%c",
2241
0
            eol);
2242
0
      } else if (!ab_info) {
2243
        /* same */
2244
0
        fprintf(s->fp, "# branch.ab +0 -0%c", eol);
2245
0
      }
2246
0
    }
2247
0
  }
2248
0
}
2249
2250
/*
2251
 * Print the stash count in a porcelain-friendly format
2252
 */
2253
static void wt_porcelain_v2_print_stash(struct wt_status *s)
2254
0
{
2255
0
  int stash_count = count_stash_entries();
2256
0
  char eol = s->null_termination ? '\0' : '\n';
2257
2258
0
  if (stash_count > 0)
2259
0
    fprintf(s->fp, "# stash %d%c", stash_count, eol);
2260
0
}
2261
2262
/*
2263
 * Convert various submodule status values into a
2264
 * fixed-length string of characters in the buffer provided.
2265
 */
2266
static void wt_porcelain_v2_submodule_state(
2267
  struct wt_status_change_data *d,
2268
  char sub[5])
2269
0
{
2270
0
  if (S_ISGITLINK(d->mode_head) ||
2271
0
    S_ISGITLINK(d->mode_index) ||
2272
0
    S_ISGITLINK(d->mode_worktree)) {
2273
0
    sub[0] = 'S';
2274
0
    sub[1] = d->new_submodule_commits ? 'C' : '.';
2275
0
    sub[2] = (d->dirty_submodule & DIRTY_SUBMODULE_MODIFIED) ? 'M' : '.';
2276
0
    sub[3] = (d->dirty_submodule & DIRTY_SUBMODULE_UNTRACKED) ? 'U' : '.';
2277
0
  } else {
2278
0
    sub[0] = 'N';
2279
0
    sub[1] = '.';
2280
0
    sub[2] = '.';
2281
0
    sub[3] = '.';
2282
0
  }
2283
0
  sub[4] = 0;
2284
0
}
2285
2286
/*
2287
 * Fix-up changed entries before we print them.
2288
 */
2289
static void wt_porcelain_v2_fix_up_changed(struct string_list_item *it)
2290
0
{
2291
0
  struct wt_status_change_data *d = it->util;
2292
2293
0
  if (!d->index_status) {
2294
    /*
2295
     * This entry is unchanged in the index (relative to the head).
2296
     * Therefore, the collect_updated_cb was never called for this
2297
     * entry (during the head-vs-index scan) and so the head column
2298
     * fields were never set.
2299
     *
2300
     * We must have data for the index column (from the
2301
     * index-vs-worktree scan (otherwise, this entry should not be
2302
     * in the list of changes)).
2303
     *
2304
     * Copy index column fields to the head column, so that our
2305
     * output looks complete.
2306
     */
2307
0
    assert(d->mode_head == 0);
2308
0
    d->mode_head = d->mode_index;
2309
0
    oidcpy(&d->oid_head, &d->oid_index);
2310
0
  }
2311
2312
0
  if (!d->worktree_status) {
2313
    /*
2314
     * This entry is unchanged in the worktree (relative to the index).
2315
     * Therefore, the collect_changed_cb was never called for this entry
2316
     * (during the index-vs-worktree scan) and so the worktree column
2317
     * fields were never set.
2318
     *
2319
     * We must have data for the index column (from the head-vs-index
2320
     * scan).
2321
     *
2322
     * Copy the index column fields to the worktree column so that
2323
     * our output looks complete.
2324
     *
2325
     * Note that we only have a mode field in the worktree column
2326
     * because the scan code tries really hard to not have to compute it.
2327
     */
2328
0
    assert(d->mode_worktree == 0);
2329
0
    d->mode_worktree = d->mode_index;
2330
0
  }
2331
0
}
2332
2333
/*
2334
 * Print porcelain v2 info for tracked entries with changes.
2335
 */
2336
static void wt_porcelain_v2_print_changed_entry(
2337
  struct string_list_item *it,
2338
  struct wt_status *s)
2339
0
{
2340
0
  struct wt_status_change_data *d = it->util;
2341
0
  struct strbuf buf = STRBUF_INIT;
2342
0
  struct strbuf buf_from = STRBUF_INIT;
2343
0
  const char *path = NULL;
2344
0
  const char *path_from = NULL;
2345
0
  char key[3];
2346
0
  char submodule_token[5];
2347
0
  char sep_char, eol_char;
2348
2349
0
  wt_porcelain_v2_fix_up_changed(it);
2350
0
  wt_porcelain_v2_submodule_state(d, submodule_token);
2351
2352
0
  key[0] = d->index_status ? d->index_status : '.';
2353
0
  key[1] = d->worktree_status ? d->worktree_status : '.';
2354
0
  key[2] = 0;
2355
2356
0
  if (s->null_termination) {
2357
    /*
2358
     * In -z mode, we DO NOT C-quote pathnames.  Current path is ALWAYS first.
2359
     * A single NUL character separates them.
2360
     */
2361
0
    sep_char = '\0';
2362
0
    eol_char = '\0';
2363
0
    path = it->string;
2364
0
    path_from = d->rename_source;
2365
0
  } else {
2366
    /*
2367
     * Path(s) are C-quoted if necessary. Current path is ALWAYS first.
2368
     * The source path is only present when necessary.
2369
     * A single TAB separates them (because paths can contain spaces
2370
     * which are not escaped and C-quoting does escape TAB characters).
2371
     */
2372
0
    sep_char = '\t';
2373
0
    eol_char = '\n';
2374
0
    path = quote_path(it->string, s->prefix, &buf, 0);
2375
0
    if (d->rename_source)
2376
0
      path_from = quote_path(d->rename_source, s->prefix, &buf_from, 0);
2377
0
  }
2378
2379
0
  if (path_from)
2380
0
    fprintf(s->fp, "2 %s %s %06o %06o %06o %s %s %c%d %s%c%s%c",
2381
0
        key, submodule_token,
2382
0
        d->mode_head, d->mode_index, d->mode_worktree,
2383
0
        oid_to_hex(&d->oid_head), oid_to_hex(&d->oid_index),
2384
0
        d->rename_status, d->rename_score,
2385
0
        path, sep_char, path_from, eol_char);
2386
0
  else
2387
0
    fprintf(s->fp, "1 %s %s %06o %06o %06o %s %s %s%c",
2388
0
        key, submodule_token,
2389
0
        d->mode_head, d->mode_index, d->mode_worktree,
2390
0
        oid_to_hex(&d->oid_head), oid_to_hex(&d->oid_index),
2391
0
        path, eol_char);
2392
2393
0
  strbuf_release(&buf);
2394
0
  strbuf_release(&buf_from);
2395
0
}
2396
2397
/*
2398
 * Print porcelain v2 status info for unmerged entries.
2399
 */
2400
static void wt_porcelain_v2_print_unmerged_entry(
2401
  struct string_list_item *it,
2402
  struct wt_status *s)
2403
0
{
2404
0
  struct wt_status_change_data *d = it->util;
2405
0
  struct index_state *istate = s->repo->index;
2406
0
  const struct cache_entry *ce;
2407
0
  struct strbuf buf_index = STRBUF_INIT;
2408
0
  const char *path_index = NULL;
2409
0
  int pos, stage, sum;
2410
0
  struct {
2411
0
    int mode;
2412
0
    struct object_id oid;
2413
0
  } stages[3];
2414
0
  const char *key;
2415
0
  char submodule_token[5];
2416
0
  char unmerged_prefix = 'u';
2417
0
  char eol_char = s->null_termination ? '\0' : '\n';
2418
2419
0
  wt_porcelain_v2_submodule_state(d, submodule_token);
2420
2421
0
  switch (d->stagemask) {
2422
0
  case 1: key = "DD"; break; /* both deleted */
2423
0
  case 2: key = "AU"; break; /* added by us */
2424
0
  case 3: key = "UD"; break; /* deleted by them */
2425
0
  case 4: key = "UA"; break; /* added by them */
2426
0
  case 5: key = "DU"; break; /* deleted by us */
2427
0
  case 6: key = "AA"; break; /* both added */
2428
0
  case 7: key = "UU"; break; /* both modified */
2429
0
  default:
2430
0
    BUG("unhandled unmerged status %x", d->stagemask);
2431
0
  }
2432
2433
  /*
2434
   * Disregard d.aux.porcelain_v2 data that we accumulated
2435
   * for the head and index columns during the scans and
2436
   * replace with the actual stage data.
2437
   *
2438
   * Note that this is a last-one-wins for each the individual
2439
   * stage [123] columns in the event of multiple cache entries
2440
   * for same stage.
2441
   */
2442
0
  memset(stages, 0, sizeof(stages));
2443
0
  sum = 0;
2444
0
  pos = index_name_pos(istate, it->string, strlen(it->string));
2445
0
  assert(pos < 0);
2446
0
  pos = -pos-1;
2447
0
  while (pos < istate->cache_nr) {
2448
0
    ce = istate->cache[pos++];
2449
0
    stage = ce_stage(ce);
2450
0
    if (strcmp(ce->name, it->string) || !stage)
2451
0
      break;
2452
0
    stages[stage - 1].mode = ce->ce_mode;
2453
0
    oidcpy(&stages[stage - 1].oid, &ce->oid);
2454
0
    sum |= (1 << (stage - 1));
2455
0
  }
2456
0
  if (sum != d->stagemask)
2457
0
    BUG("observed stagemask 0x%x != expected stagemask 0x%x", sum, d->stagemask);
2458
2459
0
  if (s->null_termination)
2460
0
    path_index = it->string;
2461
0
  else
2462
0
    path_index = quote_path(it->string, s->prefix, &buf_index, 0);
2463
2464
0
  fprintf(s->fp, "%c %s %s %06o %06o %06o %06o %s %s %s %s%c",
2465
0
      unmerged_prefix, key, submodule_token,
2466
0
      stages[0].mode, /* stage 1 */
2467
0
      stages[1].mode, /* stage 2 */
2468
0
      stages[2].mode, /* stage 3 */
2469
0
      d->mode_worktree,
2470
0
      oid_to_hex(&stages[0].oid), /* stage 1 */
2471
0
      oid_to_hex(&stages[1].oid), /* stage 2 */
2472
0
      oid_to_hex(&stages[2].oid), /* stage 3 */
2473
0
      path_index,
2474
0
      eol_char);
2475
2476
0
  strbuf_release(&buf_index);
2477
0
}
2478
2479
/*
2480
 * Print porcelain V2 status info for untracked and ignored entries.
2481
 */
2482
static void wt_porcelain_v2_print_other(
2483
  struct string_list_item *it,
2484
  struct wt_status *s,
2485
  char prefix)
2486
0
{
2487
0
  struct strbuf buf = STRBUF_INIT;
2488
0
  const char *path;
2489
0
  char eol_char;
2490
2491
0
  if (s->null_termination) {
2492
0
    path = it->string;
2493
0
    eol_char = '\0';
2494
0
  } else {
2495
0
    path = quote_path(it->string, s->prefix, &buf, 0);
2496
0
    eol_char = '\n';
2497
0
  }
2498
2499
0
  fprintf(s->fp, "%c %s%c", prefix, path, eol_char);
2500
2501
0
  strbuf_release(&buf);
2502
0
}
2503
2504
/*
2505
 * Print porcelain V2 status.
2506
 *
2507
 * [<v2_branch>]
2508
 * [<v2_changed_items>]*
2509
 * [<v2_unmerged_items>]*
2510
 * [<v2_untracked_items>]*
2511
 * [<v2_ignored_items>]*
2512
 *
2513
 */
2514
static void wt_porcelain_v2_print(struct wt_status *s)
2515
0
{
2516
0
  struct wt_status_change_data *d;
2517
0
  struct string_list_item *it;
2518
0
  int i;
2519
2520
0
  if (s->show_branch)
2521
0
    wt_porcelain_v2_print_tracking(s);
2522
2523
0
  if (s->show_stash)
2524
0
    wt_porcelain_v2_print_stash(s);
2525
2526
0
  for (i = 0; i < s->change.nr; i++) {
2527
0
    it = &(s->change.items[i]);
2528
0
    d = it->util;
2529
0
    if (!d->stagemask)
2530
0
      wt_porcelain_v2_print_changed_entry(it, s);
2531
0
  }
2532
2533
0
  for (i = 0; i < s->change.nr; i++) {
2534
0
    it = &(s->change.items[i]);
2535
0
    d = it->util;
2536
0
    if (d->stagemask)
2537
0
      wt_porcelain_v2_print_unmerged_entry(it, s);
2538
0
  }
2539
2540
0
  for (i = 0; i < s->untracked.nr; i++) {
2541
0
    it = &(s->untracked.items[i]);
2542
0
    wt_porcelain_v2_print_other(it, s, '?');
2543
0
  }
2544
2545
0
  for (i = 0; i < s->ignored.nr; i++) {
2546
0
    it = &(s->ignored.items[i]);
2547
0
    wt_porcelain_v2_print_other(it, s, '!');
2548
0
  }
2549
0
}
2550
2551
void wt_status_print(struct wt_status *s)
2552
82
{
2553
82
  trace2_data_intmax("status", s->repo, "count/changed", s->change.nr);
2554
82
  trace2_data_intmax("status", s->repo, "count/untracked",
2555
82
         s->untracked.nr);
2556
82
  trace2_data_intmax("status", s->repo, "count/ignored", s->ignored.nr);
2557
2558
82
  trace2_region_enter("status", "print", s->repo);
2559
2560
82
  switch (s->status_format) {
2561
0
  case STATUS_FORMAT_SHORT:
2562
0
    wt_shortstatus_print(s);
2563
0
    break;
2564
0
  case STATUS_FORMAT_PORCELAIN:
2565
0
    wt_porcelain_print(s);
2566
0
    break;
2567
0
  case STATUS_FORMAT_PORCELAIN_V2:
2568
0
    wt_porcelain_v2_print(s);
2569
0
    break;
2570
0
  case STATUS_FORMAT_UNSPECIFIED:
2571
0
    BUG("finalize_deferred_config() should have been called");
2572
0
    break;
2573
82
  case STATUS_FORMAT_NONE:
2574
82
  case STATUS_FORMAT_LONG:
2575
82
    wt_longstatus_print(s);
2576
82
    break;
2577
82
  }
2578
2579
82
  trace2_region_leave("status", "print", s->repo);
2580
82
}
2581
2582
/**
2583
 * Returns 1 if there are unstaged changes, 0 otherwise.
2584
 */
2585
int has_unstaged_changes(struct repository *r, int ignore_submodules)
2586
0
{
2587
0
  struct rev_info rev_info;
2588
0
  int result;
2589
2590
0
  repo_init_revisions(r, &rev_info, NULL);
2591
0
  if (ignore_submodules) {
2592
0
    rev_info.diffopt.flags.ignore_submodules = 1;
2593
0
    rev_info.diffopt.flags.override_submodule_config = 1;
2594
0
  }
2595
0
  rev_info.diffopt.flags.quick = 1;
2596
0
  diff_setup_done(&rev_info.diffopt);
2597
0
  run_diff_files(&rev_info, 0);
2598
0
  result = diff_result_code(&rev_info.diffopt);
2599
0
  release_revisions(&rev_info);
2600
0
  return result;
2601
0
}
2602
2603
/**
2604
 * Returns 1 if there are uncommitted changes, 0 otherwise.
2605
 */
2606
int has_uncommitted_changes(struct repository *r,
2607
          int ignore_submodules)
2608
0
{
2609
0
  struct rev_info rev_info;
2610
0
  int result;
2611
2612
0
  if (is_index_unborn(r->index))
2613
0
    return 0;
2614
2615
0
  repo_init_revisions(r, &rev_info, NULL);
2616
0
  if (ignore_submodules)
2617
0
    rev_info.diffopt.flags.ignore_submodules = 1;
2618
0
  rev_info.diffopt.flags.quick = 1;
2619
2620
0
  add_head_to_pending(&rev_info);
2621
0
  if (!rev_info.pending.nr) {
2622
    /*
2623
     * We have no head (or it's corrupt); use the empty tree,
2624
     * which will complain if the index is non-empty.
2625
     */
2626
0
    struct tree *tree = lookup_tree(r, the_hash_algo->empty_tree);
2627
0
    add_pending_object(&rev_info, &tree->object, "");
2628
0
  }
2629
2630
0
  diff_setup_done(&rev_info.diffopt);
2631
0
  run_diff_index(&rev_info, DIFF_INDEX_CACHED);
2632
0
  result = diff_result_code(&rev_info.diffopt);
2633
0
  release_revisions(&rev_info);
2634
0
  return result;
2635
0
}
2636
2637
/**
2638
 * If the work tree has unstaged or uncommitted changes, dies with the
2639
 * appropriate message.
2640
 */
2641
int require_clean_work_tree(struct repository *r,
2642
          const char *action,
2643
          const char *hint,
2644
          int ignore_submodules,
2645
          int gently)
2646
0
{
2647
0
  struct lock_file lock_file = LOCK_INIT;
2648
0
  int err = 0, fd;
2649
2650
0
  fd = repo_hold_locked_index(r, &lock_file, 0);
2651
0
  refresh_index(r->index, REFRESH_QUIET, NULL, NULL, NULL);
2652
0
  if (0 <= fd)
2653
0
    repo_update_index_if_able(r, &lock_file);
2654
0
  rollback_lock_file(&lock_file);
2655
2656
0
  if (has_unstaged_changes(r, ignore_submodules)) {
2657
    /* TRANSLATORS: the action is e.g. "pull with rebase" */
2658
0
    error(_("cannot %s: You have unstaged changes."), _(action));
2659
0
    err = 1;
2660
0
  }
2661
2662
0
  if (has_uncommitted_changes(r, ignore_submodules)) {
2663
0
    if (err)
2664
0
      error(_("additionally, your index contains uncommitted changes."));
2665
0
    else
2666
0
      error(_("cannot %s: Your index contains uncommitted changes."),
2667
0
            _(action));
2668
0
    err = 1;
2669
0
  }
2670
2671
0
  if (err) {
2672
0
    if (hint) {
2673
0
      if (!*hint)
2674
0
        BUG("empty hint passed to require_clean_work_tree();"
2675
0
            " use NULL instead");
2676
0
      error("%s", hint);
2677
0
    }
2678
0
    if (!gently)
2679
0
      exit(128);
2680
0
  }
2681
2682
0
  return err;
2683
0
}