Coverage Report

Created: 2026-01-09 07:10

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