struct ref {
char *name; /* Ref name; tag or head names are shortened. */
char id[SIZEOF_REV]; /* Commit SHA1 ID */
+ unsigned int head:1; /* Is it the current HEAD? */
unsigned int tag:1; /* Is it a tag? */
unsigned int ltag:1; /* If so, is the tag local? */
unsigned int remote:1; /* Is it a remote ref? */
+ unsigned int tracked:1; /* Is it the remote for the current HEAD? */
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 char opt_file[SIZEOF_STR] = "";
static char opt_ref[SIZEOF_REF] = "";
static char opt_head[SIZEOF_REF] = "";
+static char opt_remote[SIZEOF_REF] = "";
static bool opt_no_head = TRUE;
static FILE *opt_pipe = NULL;
static char opt_encoding[20] = "UTF-8";
LINE(CURSOR, "", COLOR_WHITE, COLOR_GREEN, A_BOLD), \
LINE(STATUS, "", COLOR_GREEN, COLOR_DEFAULT, 0), \
LINE(DELIMITER, "", COLOR_MAGENTA, COLOR_DEFAULT, 0), \
+LINE(LINE_NUMBER, "", COLOR_CYAN, COLOR_DEFAULT, 0), \
LINE(TITLE_BLUR, "", COLOR_WHITE, COLOR_BLUE, 0), \
LINE(TITLE_FOCUS, "", COLOR_WHITE, COLOR_BLUE, A_BOLD), \
LINE(MAIN_DATE, "", COLOR_BLUE, COLOR_DEFAULT, 0), \
LINE(MAIN_AUTHOR, "", COLOR_GREEN, COLOR_DEFAULT, 0), \
LINE(MAIN_COMMIT, "", COLOR_DEFAULT, COLOR_DEFAULT, 0), \
LINE(MAIN_TAG, "", COLOR_MAGENTA, COLOR_DEFAULT, A_BOLD), \
-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_LOCAL_TAG,"", COLOR_MAGENTA, COLOR_DEFAULT, 0), \
+LINE(MAIN_REMOTE, "", COLOR_YELLOW, COLOR_DEFAULT, 0), \
+LINE(MAIN_TRACKED, "", COLOR_YELLOW, COLOR_DEFAULT, A_BOLD), \
+LINE(MAIN_REF, "", COLOR_CYAN, COLOR_DEFAULT, 0), \
+LINE(MAIN_HEAD, "", COLOR_CYAN, 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(BLAME_DATE, "", COLOR_BLUE, COLOR_DEFAULT, 0), \
LINE(BLAME_AUTHOR, "", COLOR_GREEN, COLOR_DEFAULT, 0), \
LINE(BLAME_COMMIT, "", COLOR_DEFAULT, COLOR_DEFAULT, 0), \
-LINE(BLAME_ID, "", COLOR_MAGENTA, COLOR_DEFAULT, 0), \
-LINE(BLAME_LINENO, "", COLOR_CYAN, COLOR_DEFAULT, 0)
+LINE(BLAME_ID, "", COLOR_MAGENTA, COLOR_DEFAULT, 0)
enum line_type {
#define LINE(type, line, fg, bg, attr) \
if (max < max_number)
max_number = max;
+ if (!selected)
+ wattrset(view->win, get_line_attr(LINE_LINE_NUMBER));
col = draw_text(view, number, max_number, showtrimmed, selected);
if (col < max) {
if (!selected)
OPEN_SPLIT = 1, /* Split current view. */
OPEN_BACKGROUNDED = 2, /* Backgrounded. */
OPEN_RELOAD = 4, /* Reload view even if it is the current. */
+ OPEN_NOMAXIMIZE = 8, /* Do not maximize the current view. */
};
static void
bool backgrounded = !!(flags & OPEN_BACKGROUNDED);
bool split = !!(flags & OPEN_SPLIT);
bool reload = !!(flags & OPEN_RELOAD);
+ bool nomaximize = !!(flags & OPEN_NOMAXIMIZE);
struct view *view = VIEW(request);
int nviews = displayed_views();
struct view *base_view = display[0];
display[1] = view;
if (!backgrounded)
current_view = 1;
- } else {
+ } else if (!nomaximize) {
/* Maximize the current view. */
memset(display, 0, sizeof(display));
current_view = 0;
if (view->pipe && view->lines == 0) {
/* Clear the old view and let the incremental updating refill
* the screen. */
- wclear(view->win);
+ werase(view->win);
report("");
} else {
redraw_view(view);
if (request > REQ_NONE) {
open_run_request(request);
- return TRUE;
+ /* FIXME: When all views can refresh always do this. */
+ if (view == VIEW(REQ_VIEW_STATUS) ||
+ view == VIEW(REQ_VIEW_STAGE))
+ request = REQ_REFRESH;
+ else
+ return TRUE;
}
if (view && view->lines) {
case REQ_TOGGLE_LINENO:
opt_line_number = !opt_line_number;
- redraw_display();
+ redraw_view(view);
break;
case REQ_TOGGLE_DATE:
opt_date = !opt_date;
- redraw_display();
+ redraw_view(view);
break;
case REQ_TOGGLE_AUTHOR:
opt_author = !opt_author;
- redraw_display();
+ redraw_view(view);
break;
case REQ_TOGGLE_REV_GRAPH:
opt_rev_graph = !opt_rev_graph;
- redraw_display();
+ redraw_view(view);
break;
case REQ_TOGGLE_REFS:
opt_show_refs = !opt_show_refs;
- redraw_display();
+ redraw_view(view);
break;
case REQ_PROMPT:
if (view->lines > 0)
pipe = popen(view->cmd, "r");
+ else if (!view->parent)
+ die("No blame exist for %s", view->vid);
view->cmd[0] = 0;
if (!pipe) {
report("Failed to load blame data");
}
{
- if (!selected)
- wattrset(view->win, get_line_attr(LINE_BLAME_LINENO));
col += draw_lineno(view, lineno, view->width - col, selected);
if (col >= view->width)
return TRUE;
status_draw(struct view *view, struct line *line, unsigned int lineno, bool selected)
{
struct status *status = line->data;
+ char *text;
+ int col = 0;
wmove(view->win, lineno, 0);
}
if (!status) {
- char *text;
-
switch (line->type) {
case LINE_STAT_STAGED:
text = "Changes to be committed:";
default:
return FALSE;
}
+ } else {
+ char buf[] = { status->status, ' ', ' ', ' ', 0 };
- draw_text(view, text, view->width, TRUE, selected);
- return TRUE;
+ col += draw_text(view, buf, view->width, TRUE, selected);
+ if (!selected)
+ wattrset(view->win, A_NORMAL);
+ text = status->new.name;
}
- waddch(view->win, status->status);
- if (!selected)
- wattrset(view->win, A_NORMAL);
- wmove(view->win, lineno, 4);
- if (view->width < 5)
- return TRUE;
-
- draw_text(view, status->new.name, view->width - 5, TRUE, selected);
+ draw_text(view, text, view->width - col, TRUE, selected);
return TRUE;
}
return REQ_NONE;
}
+static bool
+status_exists(struct status *status, enum line_type type)
+{
+ struct view *view = VIEW(REQ_VIEW_STATUS);
+ struct line *line;
+
+ for (line = view->line; line < view->line + view->lines; line++) {
+ struct status *pos = line->data;
+
+ if (line->type == type && pos &&
+ !strcmp(status->new.name, pos->new.name))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
static FILE *
status_update_prepare(enum line_type type)
return written == bufsize;
}
-static struct line *
-stage_diff_hdr(struct view *view, struct line *line)
+static bool
+stage_diff_write(FILE *pipe, struct line *line, struct line *end)
{
- int diff_hdr_dir = line->type == LINE_DIFF_CHUNK ? -1 : 1;
- struct line *diff_hdr;
-
- if (line->type == LINE_DIFF_CHUNK)
- diff_hdr = line - 1;
- else
- diff_hdr = view->line + 1;
+ while (line < end) {
+ if (!stage_diff_line(pipe, line++))
+ return FALSE;
+ if (line->type == LINE_DIFF_CHUNK ||
+ line->type == LINE_DIFF_HEADER)
+ break;
+ }
- while (diff_hdr > view->line && diff_hdr < view->line + view->lines) {
- if (diff_hdr->type == LINE_DIFF_HEADER)
- return diff_hdr;
+ return TRUE;
+}
- diff_hdr += diff_hdr_dir;
- }
+static struct line *
+stage_diff_find(struct view *view, struct line *line, enum line_type type)
+{
+ for (; view->line < line; line--)
+ if (line->type == type)
+ return line;
return NULL;
}
static bool
-stage_update_chunk(struct view *view, struct line *line)
+stage_update_chunk(struct view *view, struct line *chunk)
{
char cmd[SIZEOF_STR];
size_t cmdsize = 0;
- struct line *diff_hdr, *diff_chunk, *diff_end;
+ struct line *diff_hdr;
FILE *pipe;
- diff_hdr = stage_diff_hdr(view, line);
+ diff_hdr = stage_diff_find(view, chunk, LINE_DIFF_HEADER);
if (!diff_hdr)
return FALSE;
if (!pipe)
return FALSE;
- diff_end = view->line + view->lines;
- if (line->type != LINE_DIFF_CHUNK) {
- diff_chunk = diff_hdr;
-
- } else {
- for (diff_chunk = line + 1; diff_chunk < diff_end; diff_chunk++)
- if (diff_chunk->type == LINE_DIFF_CHUNK ||
- diff_chunk->type == LINE_DIFF_HEADER)
- diff_end = diff_chunk;
-
- diff_chunk = line;
-
- while (diff_hdr->type != LINE_DIFF_CHUNK) {
- switch (diff_hdr->type) {
- case LINE_DIFF_HEADER:
- case LINE_DIFF_INDEX:
- case LINE_DIFF_ADD:
- case LINE_DIFF_DEL:
- break;
-
- default:
- diff_hdr++;
- continue;
- }
-
- if (!stage_diff_line(pipe, diff_hdr++)) {
- pclose(pipe);
- return FALSE;
- }
- }
- }
-
- while (diff_chunk < diff_end && stage_diff_line(pipe, diff_chunk))
- diff_chunk++;
+ if (!stage_diff_write(pipe, diff_hdr, chunk) ||
+ !stage_diff_write(pipe, chunk, view->line + view->lines))
+ chunk = NULL;
pclose(pipe);
- if (diff_chunk != diff_end)
- return FALSE;
-
- return TRUE;
+ return chunk ? TRUE : FALSE;
}
-static void
+static bool
stage_update(struct view *view, struct line *line)
{
- if (!opt_no_head && stage_line_type != LINE_STAT_UNTRACKED &&
- (line->type == LINE_DIFF_CHUNK || !stage_status.status)) {
- if (!stage_update_chunk(view, line)) {
+ struct line *chunk = NULL;
+
+ if (!opt_no_head && stage_line_type != LINE_STAT_UNTRACKED)
+ chunk = stage_diff_find(view, line, LINE_DIFF_CHUNK);
+
+ if (chunk) {
+ if (!stage_update_chunk(view, chunk)) {
report("Failed to apply chunk");
- return;
+ return FALSE;
}
} else if (!status_update_file(&stage_status, stage_line_type)) {
report("Failed to update file");
- return;
+ return FALSE;
}
- open_view(view, REQ_VIEW_STATUS, OPEN_RELOAD);
-
- view = VIEW(REQ_VIEW_STATUS);
- if (view_is_displayed(view))
- status_enter(view, &view->line[view->lineno]);
+ return TRUE;
}
static enum request
open_editor(stage_status.status != '?', stage_status.new.name);
break;
+ case REQ_REFRESH:
+ /* Reload everything ... */
+ break;
+
case REQ_VIEW_BLAME:
if (stage_status.new.name[0]) {
string_copy(opt_file, stage_status.new.name);
return request;
case REQ_ENTER:
- pager_request(view, request, line);
- break;
+ return pager_request(view, request, line);
default:
return request;
}
+ open_view(view, REQ_VIEW_STATUS, OPEN_RELOAD | OPEN_NOMAXIMIZE);
+
+ /* Check whether the staged entry still exists, and close the
+ * stage view if it doesn't. */
+ if (!status_exists(&stage_status, stage_line_type))
+ return REQ_VIEW_CLOSE;
+
+ if (stage_line_type == LINE_STAT_UNTRACKED)
+ opt_pipe = fopen(stage_status.new.name, "r");
+ else
+ string_copy(opt_cmd, view->cmd);
+ open_view(view, REQ_VIEW_STAGE, OPEN_RELOAD | OPEN_NOMAXIMIZE);
+
return REQ_NONE;
}
wattrset(view->win, get_line_attr(LINE_MAIN_LOCAL_TAG));
else if (commit->refs[i]->tag)
wattrset(view->win, get_line_attr(LINE_MAIN_TAG));
+ else if (commit->refs[i]->tracked)
+ wattrset(view->win, get_line_attr(LINE_MAIN_TRACKED));
else if (commit->refs[i]->remote)
wattrset(view->win, get_line_attr(LINE_MAIN_REMOTE));
else
struct commit *commit;
if (!line) {
+ if (!view->lines && !view->parent)
+ die("No revisions match the given arguments.");
update_rev_graph(graph);
return TRUE;
}
bool tag = FALSE;
bool ltag = FALSE;
bool remote = FALSE;
+ bool tracked = FALSE;
bool check_replace = FALSE;
bool head = FALSE;
remote = TRUE;
namelen -= STRING_SIZE("refs/remotes/");
name += STRING_SIZE("refs/remotes/");
+ tracked = !strcmp(opt_remote, name);
} else if (!strncmp(name, "refs/heads/", STRING_SIZE("refs/heads/"))) {
namelen -= STRING_SIZE("refs/heads/");
strncpy(ref->name, name, namelen);
ref->name[namelen] = 0;
+ ref->head = head;
ref->tag = tag;
ref->ltag = ltag;
ref->remote = remote;
- ref->head = head;
+ ref->tracked = tracked;
string_copy_rev(ref->id, id);
return OK;
if (!strcmp(name, "core.editor"))
string_ncopy(opt_editor, value, valuelen);
+ /* branch.<head>.remote */
+ if (*opt_head &&
+ !strncmp(name, "branch.", 7) &&
+ !strncmp(name + 7, opt_head, strlen(opt_head)) &&
+ !strcmp(name + 7 + strlen(opt_head), ".remote"))
+ string_ncopy(opt_remote, value, valuelen);
+
+ if (*opt_head && *opt_remote &&
+ !strncmp(name, "branch.", 7) &&
+ !strncmp(name + 7, opt_head, strlen(opt_head)) &&
+ !strcmp(name + 7 + strlen(opt_head), ".merge")) {
+ size_t from = strlen(opt_remote);
+
+ if (!strncmp(value, "refs/heads/", STRING_SIZE("refs/heads/"))) {
+ value += STRING_SIZE("refs/heads/");
+ valuelen -= STRING_SIZE("refs/heads/");
+ }
+
+ if (!string_format_from(opt_remote, &from, "/%s", value))
+ opt_remote[0] = 0;
+ }
+
return OK;
}
static int
-load_repo_config(void)
+load_git_config(void)
{
return read_properties(popen(GIT_CONFIG " --list", "r"),
"=", read_repo_config_option);
load_repo_info(void)
{
int result;
- FILE *pipe = popen("git rev-parse --git-dir --is-inside-work-tree "
- " --show-cdup --symbolic-full-name HEAD 2>/dev/null", "r");
+ FILE *pipe = popen("(git rev-parse --git-dir --is-inside-work-tree "
+ " --show-cdup; git symbolic-ref 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
if (load_options() == ERR)
die("Failed to load user config.");
- /* Load the repo config file so options can be overwritten from
- * the command line. */
- if (load_repo_config() == ERR)
+ if (load_git_config() == ERR)
die("Failed to load repo config.");
if (!parse_options(argc, argv))
die("Failed to initialize character set conversion");
}
- if (load_refs() == ERR)
+ if (*opt_git_dir && load_refs() == ERR)
die("Failed to load refs.");
for (i = 0; i < ARRAY_SIZE(views) && (view = &views[i]); i++)