unsigned int ltag:1; /* If so, is the tag local? */
unsigned int remote:1; /* Is it a remote ref? */
unsigned int next:1; /* For ref lists: are there more refs? */
+ unsigned int head:1; /* Is it the current HEAD? */
};
static struct ref **get_refs(char *id);
static enum request opt_request = REQ_VIEW_MAIN;
static char opt_cmd[SIZEOF_STR] = "";
static char opt_path[SIZEOF_STR] = "";
+static char opt_file[SIZEOF_STR] = "";
static char opt_ref[SIZEOF_REF] = "";
+static char opt_head[SIZEOF_REF] = "";
static FILE *opt_pipe = NULL;
static char opt_encoding[20] = "UTF-8";
static bool opt_utf8 = TRUE;
static signed char opt_is_inside_work_tree = -1; /* set to TRUE or FALSE */
static char opt_editor[SIZEOF_STR] = "";
-enum option_type {
- OPT_NONE,
- OPT_INT,
-};
-
-static bool
-check_option(char *opt, char short_name, char *name, enum option_type type, ...)
-{
- va_list args;
- char *value = "";
- int *number;
-
- if (opt[0] != '-')
- return FALSE;
-
- if (opt[1] == '-') {
- int namelen = strlen(name);
-
- opt += 2;
-
- if (strncmp(opt, name, namelen))
- return FALSE;
-
- if (opt[namelen] == '=')
- value = opt + namelen + 1;
-
- } else {
- if (!short_name || opt[1] != short_name)
- return FALSE;
- value = opt + 2;
- }
-
- va_start(args, type);
- if (type == OPT_INT) {
- number = va_arg(args, int *);
- if (isdigit(*value))
- *number = atoi(value);
- }
- va_end(args);
-
- return TRUE;
-}
-
-/* Returns the index of log or diff command or -1 to exit. */
static bool
parse_options(int argc, char *argv[])
{
- char *altargv[1024];
- int altargc = 0;
+ size_t buf_size;
char *subcommand;
+ bool seen_dashdash = FALSE;
int i;
if (argc <= 1)
return TRUE;
subcommand = argv[1];
- if (!strcmp(subcommand, "status")) {
+ if (!strcmp(subcommand, "status") || !strcmp(subcommand, "-S")) {
opt_request = REQ_VIEW_STATUS;
+ if (!strcmp(subcommand, "-S"))
+ warn("`-S' has been deprecated; use `tig status' instead");
if (argc > 2)
warn("ignoring arguments after `%s'", subcommand);
return TRUE;
i++;
}
- string_ncopy(opt_path, argv[i], strlen(argv[i]));
+ string_ncopy(opt_file, argv[i], strlen(argv[i]));
return TRUE;
} else if (!strcmp(subcommand, "show")) {
subcommand = NULL;
}
+ if (!subcommand)
+ /* XXX: This is vulnerable to the user overriding
+ * options required for the main view parser. */
+ string_copy(opt_cmd, "git log --no-color --pretty=raw --boundary --parents");
+ else
+ string_format(opt_cmd, "git %s", subcommand);
+
+ buf_size = strlen(opt_cmd);
+
for (i = 1 + !!subcommand; i < argc; i++) {
char *opt = argv[i];
- if (opt[0] && opt[0] != '-')
- break;
+ if (seen_dashdash || !strcmp(opt, "--")) {
+ seen_dashdash = TRUE;
- if (!strcmp(opt, "--")) {
- i++;
- break;
- }
-
- if (check_option(opt, 'v', "version", OPT_NONE)) {
+ } else if (!strcmp(opt, "-v") || !strcmp(opt, "--version")) {
printf("tig version %s\n", TIG_VERSION);
return FALSE;
- }
- if (check_option(opt, 'h', "help", OPT_NONE)) {
+ } else if (!strcmp(opt, "-h") || !strcmp(opt, "--help")) {
printf(usage);
return FALSE;
}
- if (!strcmp(opt, "-S")) {
- warn("`%s' has been deprecated; use `tig status' instead", opt);
- opt_request = REQ_VIEW_STATUS;
- continue;
- }
-
- if (!strcmp(opt, "-l")) {
- opt_request = REQ_VIEW_LOG;
- } else if (!strcmp(opt, "-d")) {
- opt_request = REQ_VIEW_DIFF;
- } else if (check_option(opt, 'n', "line-number", OPT_INT, &opt_num_interval)) {
- opt_line_number = TRUE;
- } else if (check_option(opt, 'b', "tab-size", OPT_INT, &opt_tab_size)) {
- opt_tab_size = MIN(opt_tab_size, TABSIZE);
- } else {
- if (altargc >= ARRAY_SIZE(altargv))
- die("maximum number of arguments exceeded");
- altargv[altargc++] = opt;
- continue;
- }
-
- warn("`%s' has been deprecated", opt);
+ opt_cmd[buf_size++] = ' ';
+ buf_size = sq_quote(opt_cmd, buf_size, opt);
+ if (buf_size >= sizeof(opt_cmd))
+ die("command too long");
}
if (!isatty(STDIN_FILENO)) {
opt_request = REQ_VIEW_PAGER;
opt_pipe = stdin;
-
- } else if (i < argc || altargc > 0) {
- int alti = 0;
- size_t buf_size;
-
- if (opt_request == REQ_VIEW_MAIN)
- /* XXX: This is vulnerable to the user overriding
- * options required for the main view parser. */
- string_copy(opt_cmd, "git log --no-color --pretty=raw --boundary --parents");
- else
- string_format(opt_cmd, "git %s", subcommand);
- buf_size = strlen(opt_cmd);
-
- while (buf_size < sizeof(opt_cmd) && alti < altargc) {
- opt_cmd[buf_size++] = ' ';
- buf_size = sq_quote(opt_cmd, buf_size, altargv[alti++]);
- }
-
- while (buf_size < sizeof(opt_cmd) && i < argc) {
- opt_cmd[buf_size++] = ' ';
- buf_size = sq_quote(opt_cmd, buf_size, argv[i++]);
- }
-
- if (buf_size >= sizeof(opt_cmd))
- die("command too long");
-
- opt_cmd[buf_size] = 0;
+ buf_size = 0;
}
+ opt_cmd[buf_size] = 0;
+
return TRUE;
}
LINE(MAIN_LOCAL_TAG,"", COLOR_MAGENTA, COLOR_DEFAULT, A_BOLD), \
LINE(MAIN_REMOTE, "", COLOR_YELLOW, COLOR_DEFAULT, A_BOLD), \
LINE(MAIN_REF, "", COLOR_CYAN, COLOR_DEFAULT, A_BOLD), \
+LINE(MAIN_HEAD, "", COLOR_RED, COLOR_DEFAULT, A_BOLD), \
LINE(MAIN_REVGRAPH,"", COLOR_MAGENTA, COLOR_DEFAULT, 0), \
LINE(TREE_DIR, "", COLOR_DEFAULT, COLOR_DEFAULT, A_NORMAL), \
LINE(TREE_FILE, "", COLOR_DEFAULT, COLOR_DEFAULT, A_NORMAL), \
+LINE(STAT_HEAD, "", COLOR_YELLOW, COLOR_DEFAULT, 0), \
LINE(STAT_SECTION, "", COLOR_CYAN, COLOR_DEFAULT, 0), \
LINE(STAT_NONE, "", COLOR_DEFAULT, COLOR_DEFAULT, 0), \
LINE(STAT_STAGED, "", COLOR_MAGENTA, COLOR_DEFAULT, 0), \
* might have rearranged things. */
redraw_view(view);
-
} else if (redraw_from >= 0) {
/* If this is an incremental update, redraw the previous line
* since for commits some members could have changed when
break;
case REQ_VIEW_BLAME:
- if (!opt_path[0]) {
+ if (!opt_file[0]) {
report("No file chosen, press %s to open tree view",
get_key(REQ_VIEW_TREE));
break;
}
- if (opt_path[strlen(opt_path) - 1] == '/') {
- report("Cannot show blame for directory %s", opt_path);
- break;
- }
- string_copy(opt_ref, ref_commit);
open_view(view, request, OPEN_DEFAULT);
break;
return strcmp(name1, name2);
}
+static char *
+tree_path(struct line *line)
+{
+ char *path = line->data;
+
+ return path + SIZEOF_TREE_ATTR;
+}
+
static bool
tree_read(struct view *view, char *text)
{
/* Skip "Directory ..." and ".." line. */
for (pos = 1 + !!*opt_path; pos < view->lines; pos++) {
struct line *line = &view->line[pos];
- char *path1 = ((char *) line->data) + SIZEOF_TREE_ATTR;
+ char *path1 = tree_path(line);
char *path2 = text + SIZEOF_TREE_ATTR;
int cmp = tree_compare_entry(line->type, path1, type, path2);
{
enum open_flags flags;
+ if (request == REQ_VIEW_BLAME) {
+ char *filename = tree_path(line);
+
+ if (line->type == LINE_TREE_DIR) {
+ report("Cannot show blame for directory %s", opt_path);
+ return REQ_NONE;
+ }
+
+ string_copy(opt_ref, view->vid);
+ string_format(opt_file, "%s%s", opt_path, filename);
+ return request;
+ }
if (request == REQ_TREE_PARENT) {
if (*opt_path) {
/* fake 'cd ..' */
pop_tree_stack_entry();
} else {
- char *data = line->data;
- char *basename = data + SIZEOF_TREE_ATTR;
+ char *basename = tree_path(line);
push_tree_stack_entry(basename, view->lineno);
}
static bool
blob_read(struct view *view, char *line)
{
+ if (!line)
+ return TRUE;
return add_line_text(view, line, LINE_DEFAULT) != NULL;
}
*
* Loading the blame view is a two phase job:
*
- * 1. File content is read either using opt_path from the
+ * 1. File content is read either using opt_file from the
* filesystem or using git-cat-file.
* 2. Then blame information is incrementally added by
* reading output from git-blame.
char path[SIZEOF_STR];
char ref[SIZEOF_STR] = "";
- if (sq_quote(path, 0, opt_path) >= sizeof(path))
+ if (sq_quote(path, 0, opt_file) >= sizeof(path))
return FALSE;
if (*opt_ref && sq_quote(ref, 0, opt_ref) >= sizeof(ref))
if (!string_format(view->cmd, BLAME_CAT_FILE_CMD, ref, path))
return FALSE;
} else {
- view->pipe = fopen(opt_path, "r");
+ view->pipe = fopen(opt_file, "r");
if (!view->pipe &&
!string_format(view->cmd, BLAME_CAT_FILE_CMD, "HEAD", path))
return FALSE;
if (!string_format(view->cmd, BLAME_INCREMENTAL_CMD, ref, path))
return FALSE;
- string_format(view->ref, "%s ...", opt_path);
- string_copy_rev(view->vid, opt_path);
+ string_format(view->ref, "%s ...", opt_file);
+ string_copy_rev(view->vid, opt_file);
set_nonblocking_input(TRUE);
if (view->line) {
view->offset = view->lines = view->lineno = 0;
view->line = NULL;
view->start_time = time(NULL);
+
+ return TRUE;
}
static struct blame_commit *
char *pos = text + SIZEOF_REV - 1;
size_t lineno;
size_t group;
- struct line *line;
if (strlen(text) <= SIZEOF_REV || *pos != ' ')
return NULL;
} new;
};
+static char status_onbranch[SIZEOF_STR];
static struct status stage_status;
static enum line_type stage_line_type;
view->lines = view->line_alloc = view->line_size = view->lineno = 0;
view->line = NULL;
- if (!realloc_lines(view, view->line_size + 6))
+ if (!realloc_lines(view, view->line_size + 7))
+ return FALSE;
+
+ add_line_data(view, NULL, LINE_STAT_HEAD);
+ if (!*opt_head)
+ string_copy(status_onbranch, "Not currently on any branch");
+ else if (!string_format(status_onbranch, "On branch %s", opt_head))
return FALSE;
if (!string_format(exclude, "%s/info/exclude", opt_git_dir))
return FALSE;
/* If all went well restore the previous line number to stay in
- * the context. */
+ * the context or select a line with something that can be
+ * updated. */
+ if (prev_lineno >= view->lines)
+ prev_lineno = view->lines - 1;
+ while (prev_lineno < view->lines && !view->line[prev_lineno].data)
+ prev_lineno++;
+
+ /* If the above fails, always skip the "On branch" line. */
if (prev_lineno < view->lines)
view->lineno = prev_lineno;
else
- view->lineno = view->lines - 1;
+ view->lineno = 1;
return TRUE;
}
wchgat(view->win, -1, 0, LINE_CURSOR, NULL);
tilde_attr = -1;
+ } else if (line->type == LINE_STAT_HEAD) {
+ wattrset(view->win, get_line_attr(LINE_STAT_HEAD));
+ wchgat(view->win, -1, 0, LINE_STAT_HEAD, NULL);
+
} else if (!status && line->type != LINE_STAT_NONE) {
wattrset(view->win, get_line_attr(LINE_STAT_SECTION));
wchgat(view->win, -1, 0, LINE_STAT_SECTION, NULL);
text = " (no files)";
break;
+ case LINE_STAT_HEAD:
+ text = status_onbranch;
+ break;
+
default:
return FALSE;
}
info = "Untracked file %s";
break;
+ case LINE_STAT_HEAD:
+ return REQ_NONE;
+
default:
die("line type %d not handled in switch", line->type);
}
string_add(cmd, cmdsize, "git update-index -z --add --remove --stdin");
break;
+ case LINE_STAT_HEAD:
+ return TRUE;
+
default:
die("line type %d not handled in switch", type);
}
case REQ_VIEW_BLAME:
if (status) {
- string_copy(opt_path, status->new.name);
+ string_copy(opt_file, status->new.name);
opt_ref[0] = 0;
}
return request;
text = "Press %s to stage %s for addition";
break;
+ case LINE_STAT_HEAD:
case LINE_STAT_NONE:
text = "Nothing to update";
break;
case REQ_VIEW_BLAME:
if (stage_status.new.name[0]) {
- string_copy(opt_path, stage_status.new.name);
+ string_copy(opt_file, stage_status.new.name);
opt_ref[0] = 0;
}
return request;
do {
if (type == LINE_CURSOR)
;
+ else if (commit->refs[i]->head)
+ wattrset(view->win, get_line_attr(LINE_MAIN_HEAD));
else if (commit->refs[i]->ltag)
wattrset(view->win, get_line_attr(LINE_MAIN_LOCAL_TAG));
else if (commit->refs[i]->tag)
bool ltag = FALSE;
bool remote = FALSE;
bool check_replace = FALSE;
+ bool head = FALSE;
if (!strncmp(name, "refs/tags/", STRING_SIZE("refs/tags/"))) {
if (!strcmp(name + namelen - 3, "^{}")) {
} else if (!strncmp(name, "refs/heads/", STRING_SIZE("refs/heads/"))) {
namelen -= STRING_SIZE("refs/heads/");
name += STRING_SIZE("refs/heads/");
+ head = !strncmp(opt_head, name, namelen);
} else if (!strcmp(name, "HEAD")) {
return OK;
ref->tag = tag;
ref->ltag = ltag;
ref->remote = remote;
+ ref->head = head;
string_copy_rev(ref->id, id);
return OK;
* Default to true for the unknown case. */
opt_is_inside_work_tree = strcmp(name, "false") ? TRUE : FALSE;
- } else {
+ } else if (opt_cdup[0] == ' ') {
string_ncopy(opt_cdup, name, namelen);
+ } else {
+ if (!strncmp(name, "refs/heads/", STRING_SIZE("refs/heads/"))) {
+ namelen -= STRING_SIZE("refs/heads/");
+ name += STRING_SIZE("refs/heads/");
+ string_ncopy(opt_head, name, namelen);
+ }
}
return OK;
}
-/* XXX: The line outputted by "--show-cdup" can be empty so the option
- * must be the last one! */
static int
load_repo_info(void)
{
- return read_properties(popen("git rev-parse --git-dir --is-inside-work-tree --show-cdup 2>/dev/null", "r"),
- "=", read_repo_info);
+ int result;
+ FILE *pipe = popen("git rev-parse --git-dir --is-inside-work-tree "
+ " --show-cdup --symbolic-full-name HEAD 2>/dev/null", "r");
+
+ /* XXX: The line outputted by "--show-cdup" can be empty so
+ * initialize it to something invalid to make it possible to
+ * detect whether it has been set or not. */
+ opt_cdup[0] = ' ';
+
+ result = read_properties(pipe, "=", read_repo_info);
+ if (opt_cdup[0] == ' ')
+ opt_cdup[0] = 0;
+
+ return result;
}
static int