Merge branch 'jk/diff'

* jk/diff:
  wt-status: remove extraneous newline from 'deleted:' output
  git-status: document colorization config options
  Teach runstatus about --untracked
  git-commit.sh: convert run_status to a C builtin
  Move color option parsing out of diff.c and into color.[ch]
  diff: support custom callbacks for output
This commit is contained in:
Junio C Hamano 2006-09-17 18:14:03 -07:00
commit b467fb0b90
15 changed files with 576 additions and 232 deletions

1
.gitignore vendored
View File

@ -95,6 +95,7 @@ git-rev-list
git-rev-parse git-rev-parse
git-revert git-revert
git-rm git-rm
git-runstatus
git-send-email git-send-email
git-send-pack git-send-pack
git-sh-setup git-sh-setup

View File

@ -225,6 +225,20 @@ showbranch.default::
The default set of branches for gitlink:git-show-branch[1]. The default set of branches for gitlink:git-show-branch[1].
See gitlink:git-show-branch[1]. See gitlink:git-show-branch[1].
status.color::
A boolean to enable/disable color in the output of
gitlink:git-status[1]. May be set to `true` (or `always`),
`false` (or `never`) or `auto`, in which case colors are used
only when the output is to a terminal. Defaults to false.
status.color.<slot>::
Use customized color for status colorization. `<slot>` is
one of `header` (the header text of the status message),
`updated` (files which are updated but not committed),
`changed` (files which are changed but not updated in the index),
or `untracked` (files which are not tracked by git). The values of
these variables may be specified as in diff.color.<slot>.
tar.umask:: tar.umask::
By default, gitlink:git-tar-tree[1] sets file and directories modes By default, gitlink:git-tar-tree[1] sets file and directories modes
to 0666 or 0777. While this is both useful and acceptable for projects to 0666 or 0777. While this is both useful and acceptable for projects

View File

@ -253,7 +253,8 @@ LIB_OBJS = \
tag.o tree.o usage.o config.o environment.o ctype.o copy.o \ tag.o tree.o usage.o config.o environment.o ctype.o copy.o \
fetch-clone.o revision.o pager.o tree-walk.o xdiff-interface.o \ fetch-clone.o revision.o pager.o tree-walk.o xdiff-interface.o \
write_or_die.o trace.o \ write_or_die.o trace.o \
alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \
color.o wt-status.o
BUILTIN_OBJS = \ BUILTIN_OBJS = \
builtin-add.o \ builtin-add.o \
@ -288,6 +289,7 @@ BUILTIN_OBJS = \
builtin-rev-list.o \ builtin-rev-list.o \
builtin-rev-parse.o \ builtin-rev-parse.o \
builtin-rm.o \ builtin-rm.o \
builtin-runstatus.o \
builtin-show-branch.o \ builtin-show-branch.o \
builtin-stripspace.o \ builtin-stripspace.o \
builtin-symbolic-ref.o \ builtin-symbolic-ref.o \

36
builtin-runstatus.c Normal file
View File

@ -0,0 +1,36 @@
#include "wt-status.h"
#include "cache.h"
extern int wt_status_use_color;
static const char runstatus_usage[] =
"git-runstatus [--color|--nocolor] [--amend] [--verbose]";
int cmd_runstatus(int argc, const char **argv, const char *prefix)
{
struct wt_status s;
int i;
git_config(git_status_config);
wt_status_prepare(&s);
for (i = 1; i < argc; i++) {
if (!strcmp(argv[i], "--color"))
wt_status_use_color = 1;
else if (!strcmp(argv[i], "--nocolor"))
wt_status_use_color = 0;
else if (!strcmp(argv[i], "--amend")) {
s.amend = 1;
s.reference = "HEAD^1";
}
else if (!strcmp(argv[i], "--verbose"))
s.verbose = 1;
else if (!strcmp(argv[i], "--untracked"))
s.untracked = 1;
else
usage(runstatus_usage);
}
wt_status_print(&s);
return s.commitable ? 0 : 1;
}

View File

@ -47,6 +47,7 @@ extern int cmd_repo_config(int argc, const char **argv, const char *prefix);
extern int cmd_rev_list(int argc, const char **argv, const char *prefix); extern int cmd_rev_list(int argc, const char **argv, const char *prefix);
extern int cmd_rev_parse(int argc, const char **argv, const char *prefix); extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);
extern int cmd_rm(int argc, const char **argv, const char *prefix); extern int cmd_rm(int argc, const char **argv, const char *prefix);
extern int cmd_runstatus(int argc, const char **argv, const char *prefix);
extern int cmd_show_branch(int argc, const char **argv, const char *prefix); extern int cmd_show_branch(int argc, const char **argv, const char *prefix);
extern int cmd_show(int argc, const char **argv, const char *prefix); extern int cmd_show(int argc, const char **argv, const char *prefix);
extern int cmd_stripspace(int argc, const char **argv, const char *prefix); extern int cmd_stripspace(int argc, const char **argv, const char *prefix);

176
color.c Normal file
View File

@ -0,0 +1,176 @@
#include "color.h"
#include "cache.h"
#include "git-compat-util.h"
#include <stdarg.h>
#define COLOR_RESET "\033[m"
static int parse_color(const char *name, int len)
{
static const char * const color_names[] = {
"normal", "black", "red", "green", "yellow",
"blue", "magenta", "cyan", "white"
};
char *end;
int i;
for (i = 0; i < ARRAY_SIZE(color_names); i++) {
const char *str = color_names[i];
if (!strncasecmp(name, str, len) && !str[len])
return i - 1;
}
i = strtol(name, &end, 10);
if (*name && !*end && i >= -1 && i <= 255)
return i;
return -2;
}
static int parse_attr(const char *name, int len)
{
static const int attr_values[] = { 1, 2, 4, 5, 7 };
static const char * const attr_names[] = {
"bold", "dim", "ul", "blink", "reverse"
};
int i;
for (i = 0; i < ARRAY_SIZE(attr_names); i++) {
const char *str = attr_names[i];
if (!strncasecmp(name, str, len) && !str[len])
return attr_values[i];
}
return -1;
}
void color_parse(const char *value, const char *var, char *dst)
{
const char *ptr = value;
int attr = -1;
int fg = -2;
int bg = -2;
if (!strcasecmp(value, "reset")) {
strcpy(dst, "\033[m");
return;
}
/* [fg [bg]] [attr] */
while (*ptr) {
const char *word = ptr;
int val, len = 0;
while (word[len] && !isspace(word[len]))
len++;
ptr = word + len;
while (*ptr && isspace(*ptr))
ptr++;
val = parse_color(word, len);
if (val >= -1) {
if (fg == -2) {
fg = val;
continue;
}
if (bg == -2) {
bg = val;
continue;
}
goto bad;
}
val = parse_attr(word, len);
if (val < 0 || attr != -1)
goto bad;
attr = val;
}
if (attr >= 0 || fg >= 0 || bg >= 0) {
int sep = 0;
*dst++ = '\033';
*dst++ = '[';
if (attr >= 0) {
*dst++ = '0' + attr;
sep++;
}
if (fg >= 0) {
if (sep++)
*dst++ = ';';
if (fg < 8) {
*dst++ = '3';
*dst++ = '0' + fg;
} else {
dst += sprintf(dst, "38;5;%d", fg);
}
}
if (bg >= 0) {
if (sep++)
*dst++ = ';';
if (bg < 8) {
*dst++ = '4';
*dst++ = '0' + bg;
} else {
dst += sprintf(dst, "48;5;%d", bg);
}
}
*dst++ = 'm';
}
*dst = 0;
return;
bad:
die("bad config value '%s' for variable '%s'", value, var);
}
int git_config_colorbool(const char *var, const char *value)
{
if (!value)
return 1;
if (!strcasecmp(value, "auto")) {
if (isatty(1) || (pager_in_use && pager_use_color)) {
char *term = getenv("TERM");
if (term && strcmp(term, "dumb"))
return 1;
}
return 0;
}
if (!strcasecmp(value, "never"))
return 0;
if (!strcasecmp(value, "always"))
return 1;
return git_config_bool(var, value);
}
static int color_vprintf(const char *color, const char *fmt,
va_list args, const char *trail)
{
int r = 0;
if (*color)
r += printf("%s", color);
r += vprintf(fmt, args);
if (*color)
r += printf("%s", COLOR_RESET);
if (trail)
r += printf("%s", trail);
return r;
}
int color_printf(const char *color, const char *fmt, ...)
{
va_list args;
int r;
va_start(args, fmt);
r = color_vprintf(color, fmt, args, NULL);
va_end(args);
return r;
}
int color_printf_ln(const char *color, const char *fmt, ...)
{
va_list args;
int r;
va_start(args, fmt);
r = color_vprintf(color, fmt, args, "\n");
va_end(args);
return r;
}

12
color.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef COLOR_H
#define COLOR_H
/* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */
#define COLOR_MAXLEN 24
int git_config_colorbool(const char *var, const char *value);
void color_parse(const char *var, const char *value, char *dst);
int color_printf(const char *color, const char *fmt, ...);
int color_printf_ln(const char *color, const char *fmt, ...);
#endif /* COLOR_H */

139
diff.c
View File

@ -10,6 +10,7 @@
#include "diffcore.h" #include "diffcore.h"
#include "delta.h" #include "delta.h"
#include "xdiff-interface.h" #include "xdiff-interface.h"
#include "color.h"
static int use_size_cache; static int use_size_cache;
@ -17,8 +18,7 @@ static int diff_detect_rename_default;
static int diff_rename_limit_default = -1; static int diff_rename_limit_default = -1;
static int diff_use_color_default; static int diff_use_color_default;
/* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */ static char diff_colors[][COLOR_MAXLEN] = {
static char diff_colors[][24] = {
"\033[m", /* reset */ "\033[m", /* reset */
"", /* normal */ "", /* normal */
"\033[1m", /* bold */ "\033[1m", /* bold */
@ -45,119 +45,6 @@ static int parse_diff_color_slot(const char *var, int ofs)
die("bad config variable '%s'", var); die("bad config variable '%s'", var);
} }
static int parse_color(const char *name, int len)
{
static const char * const color_names[] = {
"normal", "black", "red", "green", "yellow",
"blue", "magenta", "cyan", "white"
};
char *end;
int i;
for (i = 0; i < ARRAY_SIZE(color_names); i++) {
const char *str = color_names[i];
if (!strncasecmp(name, str, len) && !str[len])
return i - 1;
}
i = strtol(name, &end, 10);
if (*name && !*end && i >= -1 && i <= 255)
return i;
return -2;
}
static int parse_attr(const char *name, int len)
{
static const int attr_values[] = { 1, 2, 4, 5, 7 };
static const char * const attr_names[] = {
"bold", "dim", "ul", "blink", "reverse"
};
int i;
for (i = 0; i < ARRAY_SIZE(attr_names); i++) {
const char *str = attr_names[i];
if (!strncasecmp(name, str, len) && !str[len])
return attr_values[i];
}
return -1;
}
static void parse_diff_color_value(const char *value, const char *var, char *dst)
{
const char *ptr = value;
int attr = -1;
int fg = -2;
int bg = -2;
if (!strcasecmp(value, "reset")) {
strcpy(dst, "\033[m");
return;
}
/* [fg [bg]] [attr] */
while (*ptr) {
const char *word = ptr;
int val, len = 0;
while (word[len] && !isspace(word[len]))
len++;
ptr = word + len;
while (*ptr && isspace(*ptr))
ptr++;
val = parse_color(word, len);
if (val >= -1) {
if (fg == -2) {
fg = val;
continue;
}
if (bg == -2) {
bg = val;
continue;
}
goto bad;
}
val = parse_attr(word, len);
if (val < 0 || attr != -1)
goto bad;
attr = val;
}
if (attr >= 0 || fg >= 0 || bg >= 0) {
int sep = 0;
*dst++ = '\033';
*dst++ = '[';
if (attr >= 0) {
*dst++ = '0' + attr;
sep++;
}
if (fg >= 0) {
if (sep++)
*dst++ = ';';
if (fg < 8) {
*dst++ = '3';
*dst++ = '0' + fg;
} else {
dst += sprintf(dst, "38;5;%d", fg);
}
}
if (bg >= 0) {
if (sep++)
*dst++ = ';';
if (bg < 8) {
*dst++ = '4';
*dst++ = '0' + bg;
} else {
dst += sprintf(dst, "48;5;%d", bg);
}
}
*dst++ = 'm';
}
*dst = 0;
return;
bad:
die("bad config value '%s' for variable '%s'", value, var);
}
/* /*
* These are to give UI layer defaults. * These are to give UI layer defaults.
* The core-level commands such as git-diff-files should * The core-level commands such as git-diff-files should
@ -171,22 +58,7 @@ int git_diff_ui_config(const char *var, const char *value)
return 0; return 0;
} }
if (!strcmp(var, "diff.color")) { if (!strcmp(var, "diff.color")) {
if (!value) diff_use_color_default = git_config_colorbool(var, value);
diff_use_color_default = 1; /* bool */
else if (!strcasecmp(value, "auto")) {
diff_use_color_default = 0;
if (isatty(1) || (pager_in_use && pager_use_color)) {
char *term = getenv("TERM");
if (term && strcmp(term, "dumb"))
diff_use_color_default = 1;
}
}
else if (!strcasecmp(value, "never"))
diff_use_color_default = 0;
else if (!strcasecmp(value, "always"))
diff_use_color_default = 1;
else
diff_use_color_default = git_config_bool(var, value);
return 0; return 0;
} }
if (!strcmp(var, "diff.renames")) { if (!strcmp(var, "diff.renames")) {
@ -201,7 +73,7 @@ int git_diff_ui_config(const char *var, const char *value)
} }
if (!strncmp(var, "diff.color.", 11)) { if (!strncmp(var, "diff.color.", 11)) {
int slot = parse_diff_color_slot(var, 11); int slot = parse_diff_color_slot(var, 11);
parse_diff_color_value(value, var, diff_colors[slot]); color_parse(value, var, diff_colors[slot]);
return 0; return 0;
} }
return git_default_config(var, value); return git_default_config(var, value);
@ -2593,6 +2465,9 @@ void diff_flush(struct diff_options *options)
} }
} }
if (output_format & DIFF_FORMAT_CALLBACK)
options->format_callback(q, options, options->format_callback_data);
for (i = 0; i < q->nr; i++) for (i = 0; i < q->nr; i++)
diff_free_filepair(q->queue[i]); diff_free_filepair(q->queue[i]);
free_queue: free_queue:

8
diff.h
View File

@ -8,6 +8,7 @@
struct rev_info; struct rev_info;
struct diff_options; struct diff_options;
struct diff_queue_struct;
typedef void (*change_fn_t)(struct diff_options *options, typedef void (*change_fn_t)(struct diff_options *options,
unsigned old_mode, unsigned new_mode, unsigned old_mode, unsigned new_mode,
@ -20,6 +21,9 @@ typedef void (*add_remove_fn_t)(struct diff_options *options,
const unsigned char *sha1, const unsigned char *sha1,
const char *base, const char *path); const char *base, const char *path);
typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,
struct diff_options *options, void *data);
#define DIFF_FORMAT_RAW 0x0001 #define DIFF_FORMAT_RAW 0x0001
#define DIFF_FORMAT_DIFFSTAT 0x0002 #define DIFF_FORMAT_DIFFSTAT 0x0002
#define DIFF_FORMAT_SUMMARY 0x0004 #define DIFF_FORMAT_SUMMARY 0x0004
@ -35,6 +39,8 @@ typedef void (*add_remove_fn_t)(struct diff_options *options,
*/ */
#define DIFF_FORMAT_NO_OUTPUT 0x0080 #define DIFF_FORMAT_NO_OUTPUT 0x0080
#define DIFF_FORMAT_CALLBACK 0x0100
struct diff_options { struct diff_options {
const char *filter; const char *filter;
const char *orderfile; const char *orderfile;
@ -68,6 +74,8 @@ struct diff_options {
int *pathlens; int *pathlens;
change_fn_t change; change_fn_t change;
add_remove_fn_t add_remove; add_remove_fn_t add_remove;
diff_format_fn_t format_callback;
void *format_callback_data;
}; };
enum color_diff { enum color_diff {

7
dir.c
View File

@ -397,3 +397,10 @@ int read_directory(struct dir_struct *dir, const char *path, const char *base, i
qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name); qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
return dir->nr; return dir->nr;
} }
int
file_exists(const char *f)
{
struct stat sb;
return stat(f, &sb) == 0;
}

1
dir.h
View File

@ -47,5 +47,6 @@ extern int excluded(struct dir_struct *, const char *);
extern void add_excludes_from_file(struct dir_struct *, const char *fname); extern void add_excludes_from_file(struct dir_struct *, const char *fname);
extern void add_exclude(const char *string, const char *base, extern void add_exclude(const char *string, const char *base,
int baselen, struct exclude_list *which); int baselen, struct exclude_list *which);
extern int file_exists(const char *);
#endif #endif

View File

@ -60,26 +60,6 @@ report () {
} }
run_status () { run_status () {
(
# We always show status for the whole tree.
cd "$TOP"
IS_INITIAL="$initial_commit"
REFERENCE=HEAD
case "$amend" in
t)
# If we are amending the initial commit, there
# is no HEAD^1.
if git-rev-parse --verify "HEAD^1" >/dev/null 2>&1
then
REFERENCE="HEAD^1"
IS_INITIAL=
else
IS_INITIAL=t
fi
;;
esac
# If TMP_INDEX is defined, that means we are doing # If TMP_INDEX is defined, that means we are doing
# "--only" partial commit, and that index file is used # "--only" partial commit, and that index file is used
# to build the tree for the commit. Otherwise, if # to build the tree for the commit. Otherwise, if
@ -96,85 +76,14 @@ run_status () {
export GIT_INDEX_FILE export GIT_INDEX_FILE
fi fi
case "$branch" in case "$status_only" in
refs/heads/master) ;; t) color= ;;
*) echo "# On branch $branch" ;; *) color=--nocolor ;;
esac esac
git-runstatus ${color} \
if test -z "$IS_INITIAL" ${verbose:+--verbose} \
then ${amend:+--amend} \
git-diff-index -M --cached --name-status \ ${untracked_files:+--untracked}
--diff-filter=MDTCRA $REFERENCE |
sed -e '
s/\\/\\\\/g
s/ /\\ /g
' |
report "Updated but not checked in" "will commit"
committable="$?"
else
echo '#
# Initial commit
#'
git-ls-files |
sed -e '
s/\\/\\\\/g
s/ /\\ /g
s/^/A /
' |
report "Updated but not checked in" "will commit"
committable="$?"
fi
git-diff-files --name-status |
sed -e '
s/\\/\\\\/g
s/ /\\ /g
' |
report "Changed but not updated" \
"use git-update-index to mark for commit"
option=""
if test -z "$untracked_files"; then
option="--directory --no-empty-directory"
fi
hdr_shown=
if test -f "$GIT_DIR/info/exclude"
then
git-ls-files --others $option \
--exclude-from="$GIT_DIR/info/exclude" \
--exclude-per-directory=.gitignore
else
git-ls-files --others $option \
--exclude-per-directory=.gitignore
fi |
while read line; do
if [ -z "$hdr_shown" ]; then
echo '#'
echo '# Untracked files:'
echo '# (use "git add" to add to commit)'
echo '#'
hdr_shown=1
fi
echo "# $line"
done
if test -n "$verbose" -a -z "$IS_INITIAL"
then
git-diff-index --cached -M -p --diff-filter=MDTCRA $REFERENCE
fi
case "$committable" in
0)
case "$amend" in
t)
echo "# No changes" ;;
*)
echo "nothing to commit" ;;
esac
exit 1 ;;
esac
exit 0
)
} }
trap ' trap '

1
git.c
View File

@ -253,6 +253,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
{ "rev-list", cmd_rev_list, RUN_SETUP }, { "rev-list", cmd_rev_list, RUN_SETUP },
{ "rev-parse", cmd_rev_parse, RUN_SETUP }, { "rev-parse", cmd_rev_parse, RUN_SETUP },
{ "rm", cmd_rm, RUN_SETUP }, { "rm", cmd_rm, RUN_SETUP },
{ "runstatus", cmd_runstatus, RUN_SETUP },
{ "show-branch", cmd_show_branch, RUN_SETUP }, { "show-branch", cmd_show_branch, RUN_SETUP },
{ "show", cmd_show, RUN_SETUP | USE_PAGER }, { "show", cmd_show, RUN_SETUP | USE_PAGER },
{ "stripspace", cmd_stripspace }, { "stripspace", cmd_stripspace },

276
wt-status.c Normal file
View File

@ -0,0 +1,276 @@
#include "wt-status.h"
#include "color.h"
#include "cache.h"
#include "object.h"
#include "dir.h"
#include "commit.h"
#include "diff.h"
#include "revision.h"
#include "diffcore.h"
int wt_status_use_color = 0;
static char wt_status_colors[][COLOR_MAXLEN] = {
"", /* WT_STATUS_HEADER: normal */
"\033[32m", /* WT_STATUS_UPDATED: green */
"\033[31m", /* WT_STATUS_CHANGED: red */
"\033[31m", /* WT_STATUS_UNTRACKED: red */
};
static int parse_status_slot(const char *var, int offset)
{
if (!strcasecmp(var+offset, "header"))
return WT_STATUS_HEADER;
if (!strcasecmp(var+offset, "updated"))
return WT_STATUS_UPDATED;
if (!strcasecmp(var+offset, "changed"))
return WT_STATUS_CHANGED;
if (!strcasecmp(var+offset, "untracked"))
return WT_STATUS_UNTRACKED;
die("bad config variable '%s'", var);
}
static const char* color(int slot)
{
return wt_status_use_color ? wt_status_colors[slot] : "";
}
void wt_status_prepare(struct wt_status *s)
{
unsigned char sha1[20];
const char *head;
s->is_initial = get_sha1("HEAD", sha1) ? 1 : 0;
head = resolve_ref(git_path("HEAD"), sha1, 0);
s->branch = head ?
strdup(head + strlen(get_git_dir()) + 1) :
NULL;
s->reference = "HEAD";
s->amend = 0;
s->verbose = 0;
s->commitable = 0;
s->untracked = 0;
}
static void wt_status_print_header(const char *main, const char *sub)
{
const char *c = color(WT_STATUS_HEADER);
color_printf_ln(c, "# %s:", main);
color_printf_ln(c, "# (%s)", sub);
color_printf_ln(c, "#");
}
static void wt_status_print_trailer(void)
{
color_printf_ln(color(WT_STATUS_HEADER), "#");
}
static void wt_status_print_filepair(int t, struct diff_filepair *p)
{
const char *c = color(t);
color_printf(color(WT_STATUS_HEADER), "#\t");
switch (p->status) {
case DIFF_STATUS_ADDED:
color_printf(c, "new file: %s", p->one->path); break;
case DIFF_STATUS_COPIED:
color_printf(c, "copied: %s -> %s",
p->one->path, p->two->path);
break;
case DIFF_STATUS_DELETED:
color_printf(c, "deleted: %s", p->one->path); break;
case DIFF_STATUS_MODIFIED:
color_printf(c, "modified: %s", p->one->path); break;
case DIFF_STATUS_RENAMED:
color_printf(c, "renamed: %s -> %s",
p->one->path, p->two->path);
break;
case DIFF_STATUS_TYPE_CHANGED:
color_printf(c, "typechange: %s", p->one->path); break;
case DIFF_STATUS_UNKNOWN:
color_printf(c, "unknown: %s", p->one->path); break;
case DIFF_STATUS_UNMERGED:
color_printf(c, "unmerged: %s", p->one->path); break;
default:
die("bug: unhandled diff status %c", p->status);
}
printf("\n");
}
static void wt_status_print_updated_cb(struct diff_queue_struct *q,
struct diff_options *options,
void *data)
{
struct wt_status *s = data;
int shown_header = 0;
int i;
if (q->nr) {
}
for (i = 0; i < q->nr; i++) {
if (q->queue[i]->status == 'U')
continue;
if (!shown_header) {
wt_status_print_header("Updated but not checked in",
"will commit");
s->commitable = 1;
shown_header = 1;
}
wt_status_print_filepair(WT_STATUS_UPDATED, q->queue[i]);
}
if (shown_header)
wt_status_print_trailer();
}
static void wt_status_print_changed_cb(struct diff_queue_struct *q,
struct diff_options *options,
void *data)
{
int i;
if (q->nr)
wt_status_print_header("Changed but not updated",
"use git-update-index to mark for commit");
for (i = 0; i < q->nr; i++)
wt_status_print_filepair(WT_STATUS_CHANGED, q->queue[i]);
if (q->nr)
wt_status_print_trailer();
}
void wt_status_print_initial(struct wt_status *s)
{
int i;
read_cache();
if (active_nr) {
s->commitable = 1;
wt_status_print_header("Updated but not checked in",
"will commit");
}
for (i = 0; i < active_nr; i++) {
color_printf(color(WT_STATUS_HEADER), "#\t");
color_printf_ln(color(WT_STATUS_UPDATED), "new file: %s",
active_cache[i]->name);
}
if (active_nr)
wt_status_print_trailer();
}
static void wt_status_print_updated(struct wt_status *s)
{
struct rev_info rev;
const char *argv[] = { NULL, NULL, NULL };
argv[1] = s->reference;
init_revisions(&rev, NULL);
setup_revisions(2, argv, &rev, NULL);
rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
rev.diffopt.format_callback = wt_status_print_updated_cb;
rev.diffopt.format_callback_data = s;
rev.diffopt.detect_rename = 1;
run_diff_index(&rev, 1);
}
static void wt_status_print_changed(struct wt_status *s)
{
struct rev_info rev;
const char *argv[] = { NULL, NULL };
init_revisions(&rev, "");
setup_revisions(1, argv, &rev, NULL);
rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
rev.diffopt.format_callback = wt_status_print_changed_cb;
rev.diffopt.format_callback_data = s;
run_diff_files(&rev, 0);
}
static void wt_status_print_untracked(const struct wt_status *s)
{
struct dir_struct dir;
const char *x;
int i;
int shown_header = 0;
memset(&dir, 0, sizeof(dir));
dir.exclude_per_dir = ".gitignore";
if (!s->untracked) {
dir.show_other_directories = 1;
dir.hide_empty_directories = 1;
}
x = git_path("info/exclude");
if (file_exists(x))
add_excludes_from_file(&dir, x);
read_directory(&dir, ".", "", 0);
for(i = 0; i < dir.nr; i++) {
/* check for matching entry, which is unmerged; lifted from
* builtin-ls-files:show_other_files */
struct dir_entry *ent = dir.entries[i];
int pos = cache_name_pos(ent->name, ent->len);
struct cache_entry *ce;
if (0 <= pos)
die("bug in wt_status_print_untracked");
pos = -pos - 1;
if (pos < active_nr) {
ce = active_cache[pos];
if (ce_namelen(ce) == ent->len &&
!memcmp(ce->name, ent->name, ent->len))
continue;
}
if (!shown_header) {
wt_status_print_header("Untracked files",
"use \"git add\" to add to commit");
shown_header = 1;
}
color_printf(color(WT_STATUS_HEADER), "#\t");
color_printf_ln(color(WT_STATUS_UNTRACKED), "%.*s",
ent->len, ent->name);
}
}
static void wt_status_print_verbose(struct wt_status *s)
{
struct rev_info rev;
const char *argv[] = { NULL, NULL, NULL };
argv[1] = s->reference;
init_revisions(&rev, NULL);
setup_revisions(2, argv, &rev, NULL);
rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
rev.diffopt.detect_rename = 1;
run_diff_index(&rev, 1);
}
void wt_status_print(struct wt_status *s)
{
if (s->branch && strcmp(s->branch, "refs/heads/master"))
color_printf_ln(color(WT_STATUS_HEADER),
"# On branch %s", s->branch);
if (s->is_initial) {
color_printf_ln(color(WT_STATUS_HEADER), "#");
color_printf_ln(color(WT_STATUS_HEADER), "# Initial commit");
color_printf_ln(color(WT_STATUS_HEADER), "#");
wt_status_print_initial(s);
}
else {
wt_status_print_updated(s);
discard_cache();
}
wt_status_print_changed(s);
wt_status_print_untracked(s);
if (s->verbose && !s->is_initial)
wt_status_print_verbose(s);
if (!s->commitable)
printf("%s\n", s->amend ? "# No changes" : "nothing to commit");
}
int git_status_config(const char *k, const char *v)
{
if (!strcmp(k, "status.color")) {
wt_status_use_color = git_config_colorbool(k, v);
return 0;
}
if (!strncmp(k, "status.color.", 13)) {
int slot = parse_status_slot(k, 13);
color_parse(v, k, wt_status_colors[slot]);
}
return git_default_config(k, v);
}

25
wt-status.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef STATUS_H
#define STATUS_H
enum color_wt_status {
WT_STATUS_HEADER,
WT_STATUS_UPDATED,
WT_STATUS_CHANGED,
WT_STATUS_UNTRACKED,
};
struct wt_status {
int is_initial;
char *branch;
const char *reference;
int commitable;
int verbose;
int amend;
int untracked;
};
int git_status_config(const char *var, const char *value);
void wt_status_prepare(struct wt_status *s);
void wt_status_print(struct wt_status *s);
#endif /* STATUS_H */