index 97794b0e6e0d85793b678537aff32e65902c5fe7..180661a4fcfbc94067a795898df1a6fc6a6e0330 100644 (file)
--- a/tig.c
+++ b/tig.c
@@ -109,7 +109,6 @@ static size_t utf8_length(const char **string, size_t col, int *width, size_t ma
/* The default interval between line numbers. */
#define NUMBER_INTERVAL 5
-#define SCROLL_INTERVAL 1
#define TAB_SIZE 8
@@ -117,6 +116,8 @@ static size_t utf8_length(const char **string, size_t col, int *width, size_t ma
#define NULL_ID "0000000000000000000000000000000000000000"
+#define S_ISGITLINK(mode) (((mode) & S_IFMT) == 0160000)
+
#ifndef GIT_CONFIG
#define GIT_CONFIG "config"
#endif
#define string_add(dst, from, src) \
string_ncopy_do(dst + (from), sizeof(dst) - (from), src, sizeof(src))
-static size_t
-string_expand_length(const char *line, int tabsize)
-{
- size_t size, pos;
-
- for (size = pos = 0; line[pos]; pos++) {
- if (line[pos] == '\t' && tabsize > 0)
- size += tabsize - (size % tabsize);
- else
- size++;
- }
- return size;
-}
-
static void
string_expand(char *dst, size_t dstlen, const char *src, int tabsize)
{
#define ENUM_MAP(name, value) { name, STRING_SIZE(name), value }
static bool
-map_enum_do(struct enum_map *map, size_t map_size, int *value, const char *name)
+map_enum_do(const struct enum_map *map, size_t map_size, int *value, const char *name)
{
size_t namelen = strlen(name);
int i;
{
init_io(io, NULL, IO_FD);
io->pipe = *name ? open(name, O_RDONLY) : STDIN_FILENO;
+ if (io->pipe == -1)
+ io->error = errno;
return io->pipe != -1;
}
return io->error;
}
-static bool
+static char *
io_strerror(struct io *io)
{
return strerror(io->error);
const char *help;
};
-static struct request_info req_info[] = {
+static const struct request_info req_info[] = {
#define REQ_GROUP(help) { 0, NULL, 0, (help) },
#define REQ_(req, help) { REQ_##req, (#req), STRING_SIZE(#req), (help) }
REQ_INFO
static bool opt_rev_graph = FALSE;
static bool opt_show_refs = TRUE;
static int opt_num_interval = NUMBER_INTERVAL;
+static double opt_hscroll = 0.50;
static int opt_tab_size = TAB_SIZE;
static int opt_author_cols = AUTHOR_COLS-1;
static char opt_path[SIZEOF_STR] = "";
enum request request;
};
-static struct keybinding default_keybindings[] = {
+static const struct keybinding default_keybindings[] = {
/* View switching */
{ 'm', REQ_VIEW_MAIN },
{ 'd', REQ_VIEW_DIFF },
#undef KEYMAP_
};
-static struct enum_map keymap_table[] = {
+static const struct enum_map keymap_table[] = {
#define KEYMAP_(name) ENUM_MAP(#name, KEYMAP_##name)
KEYMAP_INFO
#undef KEYMAP_
int value;
};
-static struct key key_table[] = {
+static const struct key key_table[] = {
{ "Enter", KEY_RETURN },
{ "Space", ' ' },
{ "Backspace", KEY_BACKSPACE },
buf[pos] = 0;
for (i = 0; i < ARRAY_SIZE(default_keybindings); i++) {
- struct keybinding *keybinding = &default_keybindings[i];
+ const struct keybinding *keybinding = &default_keybindings[i];
if (keybinding->request != request)
continue;
* User config file handling.
*/
-static struct enum_map color_map[] = {
+static int config_lineno;
+static bool config_errors;
+static const char *config_msg;
+
+static const struct enum_map color_map[] = {
#define COLOR_MAP(name) ENUM_MAP(#name, COLOR_##name)
COLOR_MAP(DEFAULT),
COLOR_MAP(BLACK),
COLOR_MAP(YELLOW),
};
-static struct enum_map attr_map[] = {
+static const struct enum_map attr_map[] = {
#define ATTR_MAP(name) ENUM_MAP(#name, A_##name)
ATTR_MAP(NORMAL),
ATTR_MAP(BLINK),
#define set_attribute(attr, name) map_enum(attr, attr_map, name)
+static int parse_step(double *opt, const char *arg)
+{
+ *opt = atoi(arg);
+ if (!strchr(arg, '%'))
+ return OK;
+
+ /* "Shift down" so 100% and 1 does not conflict. */
+ *opt = (*opt - 1) / 100;
+ if (*opt >= 1.0) {
+ *opt = 0.99;
+ config_msg = "Step value larger than 100%";
+ return ERR;
+ }
+ if (*opt < 0.0) {
+ *opt = 1;
+ config_msg = "Invalid step value";
+ return ERR;
+ }
+ return OK;
+}
+
static int
parse_int(int *opt, const char *arg, int min, int max)
{
int value = atoi(arg);
- if (min <= value && value <= max)
+ if (min <= value && value <= max) {
*opt = value;
- return OK;
+ return OK;
+ }
+
+ config_msg = "Integer value out of bound";
+ return ERR;
}
static bool
return FALSE;
}
-static int config_lineno;
-static bool config_errors;
-static const char *config_msg;
-
/* Wants: object fgcolor bgcolor [attribute] */
static int
option_color_command(int argc, const char *argv[])
info = get_line_info(argv[0]);
if (!info) {
- static struct enum_map obsolete[] = {
+ static const struct enum_map obsolete[] = {
ENUM_MAP("main-delim", LINE_DELIMITER),
ENUM_MAP("main-date", LINE_DATE),
ENUM_MAP("main-author", LINE_AUTHOR),
}
arg += 1; arglen -= 2;
default:
- string_ncopy_do(opt, optsize, arg, strlen(arg));
+ string_ncopy_do(opt, optsize, arg, arglen);
return OK;
}
}
if (!strcmp(argv[0], "author-width"))
return parse_int(&opt_author_cols, argv[2], 0, 1024);
+ if (!strcmp(argv[0], "horizontal-scroll"))
+ return parse_step(&opt_hscroll, argv[2]);
+
if (!strcmp(argv[0], "tab-size"))
return parse_int(&opt_tab_size, argv[2], 1, 1024);
request = get_request(argv[2]);
if (request == REQ_NONE) {
- static struct enum_map obsolete[] = {
+ static const struct enum_map obsolete[] = {
ENUM_MAP("cherry-pick", REQ_NONE),
ENUM_MAP("screen-resize", REQ_NONE),
ENUM_MAP("tree-parent", REQ_PARENT),
enum line_type curtype; /* Attribute currently used for drawing. */
unsigned long col; /* Column when drawing. */
bool has_scrolled; /* View was scrolled. */
- bool can_hscroll; /* View can be scrolled horizontally. */
/* Loading */
struct io io;
LINE_GRAPHIC_VLINE
};
-static int line_graphics[] = {
+static chtype line_graphics[] = {
/* LINE_GRAPHIC_VLINE: */ '|'
};
col++;
}
- if (view->col + col >= view->width + view->yoffset)
- view->can_hscroll = TRUE;
-
return col;
}
while (spaces > 0) {
int len = MIN(spaces, sizeof(space) - 1);
- col += draw_chars(view, type, space, spaces, FALSE);
+ col += draw_chars(view, type, space, len, FALSE);
spaces -= len;
}
return col;
}
-static bool
-draw_lineno(struct view *view, unsigned int lineno)
-{
- size_t skip = view->yoffset > view->col ? view->yoffset - view->col : 0;
- char number[10];
- int digits3 = view->digits < 3 ? 3 : view->digits;
- int max_number = MIN(digits3, STRING_SIZE(number));
- int max = view->width - view->col;
- int col;
-
- if (max < max_number)
- max_number = max;
-
- lineno += view->offset + 1;
- if (lineno == 1 || (lineno % opt_num_interval) == 0) {
- static char fmt[] = "%1ld";
-
- if (view->digits <= 9)
- fmt[1] = '0' + digits3;
-
- if (!string_format(number, fmt, lineno))
- number[0] = 0;
- col = draw_chars(view, LINE_LINE_NUMBER, number, max_number, TRUE);
- } else {
- col = draw_space(view, LINE_LINE_NUMBER, max_number, max_number);
- }
-
- if (col < max && skip <= col) {
- set_view_attr(view, LINE_DEFAULT);
- waddch(view->win, line_graphics[LINE_GRAPHIC_VLINE]);
- }
- col++;
-
- view->col += col;
- if (col < max && skip <= col)
- col = draw_space(view, LINE_DEFAULT, max - col, 1);
- view->col++;
-
- return view->width + view->yoffset <= view->col;
-}
-
static bool
draw_text(struct view *view, enum line_type type, const char *string, bool trim)
{
view->col += draw_chars(view, type, string, view->width + view->yoffset - view->col, trim);
- return view->width - view->col <= 0;
+ return view->width + view->yoffset <= view->col;
}
static bool
draw_graphic(struct view *view, enum line_type type, chtype graphic[], size_t size)
{
size_t skip = view->yoffset > view->col ? view->yoffset - view->col : 0;
- int max = view->width - view->col;
+ int max = view->width + view->yoffset - view->col;
int i;
if (max < size)
@@ -1967,13 +1940,13 @@ draw_graphic(struct view *view, enum line_type type, chtype graphic[], size_t si
waddch(view->win, ' ');
view->col++;
- return view->width - view->col <= 0;
+ return view->width + view->yoffset <= view->col;
}
static bool
draw_field(struct view *view, enum line_type type, const char *text, int len, bool trim)
{
- int max = MIN(view->width - view->col, len);
+ int max = MIN(view->width + view->yoffset - view->col, len);
int col;
if (text)
static bool
draw_mode(struct view *view, mode_t mode)
{
- static const char dir_mode[] = "drwxr-xr-x";
- static const char link_mode[] = "lrwxrwxrwx";
- static const char exe_mode[] = "-rwxr-xr-x";
- static const char file_mode[] = "-rw-r--r--";
const char *str;
if (S_ISDIR(mode))
- str = dir_mode;
+ str = "drwxr-xr-x";
else if (S_ISLNK(mode))
- str = link_mode;
- else if (mode & S_IXUSR)
- str = exe_mode;
+ str = "lrwxrwxrwx";
+ else if (S_ISGITLINK(mode))
+ str = "m---------";
+ else if (S_ISREG(mode) && mode & S_IXUSR)
+ str = "-rwxr-xr-x";
+ else if (S_ISREG(mode))
+ str = "-rw-r--r--";
else
- str = file_mode;
+ str = "----------";
+
+ return draw_field(view, LINE_MODE, str, STRING_SIZE("-rw-r--r-- "), FALSE);
+}
+
+static bool
+draw_lineno(struct view *view, unsigned int lineno)
+{
+ char number[10];
+ int digits3 = view->digits < 3 ? 3 : view->digits;
+ int max = MIN(view->width + view->yoffset - view->col, digits3);
+ char *text = NULL;
+
+ lineno += view->offset + 1;
+ if (lineno == 1 || (lineno % opt_num_interval) == 0) {
+ static char fmt[] = "%1ld";
- return draw_field(view, LINE_MODE, str, sizeof(file_mode), FALSE);
+ fmt[1] = '0' + (view->digits <= 9 ? digits3 : 1);
+ if (string_format(number, fmt, lineno))
+ text = number;
+ }
+ if (text)
+ view->col += draw_chars(view, LINE_LINE_NUMBER, text, max, TRUE);
+ else
+ view->col += draw_space(view, LINE_LINE_NUMBER, max, digits3);
+ return draw_graphic(view, LINE_DEFAULT, &line_graphics[LINE_GRAPHIC_VLINE], 1);
}
static bool
{
assert(0 <= lineno && lineno < view->height);
- if (lineno == 0)
- view->can_hscroll = FALSE;
-
for (; lineno < view->height; lineno++) {
if (!draw_view_line(view, lineno))
break;
report("%sabling %s", *option ? "En" : "Dis", help);
}
+static void
+maximize_view(struct view *view)
+{
+ memset(display, 0, sizeof(display));
+ current_view = 0;
+ display[current_view] = view;
+ resize_display();
+ redraw_display(FALSE);
+ report("");
+}
+
+
/*
* Navigation
*/
+static bool
+goto_view_line(struct view *view, unsigned long offset, unsigned long lineno)
+{
+ if (lineno >= view->lines)
+ lineno = view->lines > 0 ? view->lines - 1 : 0;
+
+ if (offset > lineno || offset + view->height <= lineno) {
+ unsigned long half = view->height / 2;
+
+ if (lineno > half)
+ offset = lineno - half;
+ else
+ offset = 0;
+ }
+
+ if (offset != view->offset || lineno != view->lineno) {
+ view->offset = offset;
+ view->lineno = lineno;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static int
+apply_step(double step, int value)
+{
+ if (step >= 1)
+ return (int) step;
+ value *= step + 0.01;
+ return value ? value : 1;
+}
+
/* Scrolling backend */
static void
do_scroll_view(struct view *view, int lines)
report("Cannot scroll beyond the first column");
return;
}
- if (view->yoffset <= SCROLL_INTERVAL)
+ if (view->yoffset <= apply_step(opt_hscroll, view->width))
view->yoffset = 0;
else
- view->yoffset -= SCROLL_INTERVAL;
+ view->yoffset -= apply_step(opt_hscroll, view->width);
redraw_view_from(view, 0);
report("");
return;
case REQ_SCROLL_RIGHT:
- if (!view->can_hscroll) {
- report("Cannot scroll beyond the last column");
- return;
- }
- view->yoffset += SCROLL_INTERVAL;
+ view->yoffset += apply_step(opt_hscroll, view->width);
redraw_view(view);
report("");
return;
static void
select_view_line(struct view *view, unsigned long lineno)
{
- if (lineno - view->offset >= view->height) {
- view->offset = lineno;
- view->lineno = lineno;
- if (view_is_displayed(view))
- redraw_view(view);
-
- } else {
- unsigned long old_lineno = view->lineno - view->offset;
+ unsigned long old_lineno = view->lineno;
+ unsigned long old_offset = view->offset;
- view->lineno = lineno;
+ if (goto_view_line(view, view->offset, lineno)) {
if (view_is_displayed(view)) {
- draw_view_line(view, old_lineno);
- draw_view_line(view, view->lineno - view->offset);
- wnoutrefresh(view->win);
+ if (old_offset != view->offset) {
+ redraw_view(view);
+ } else {
+ draw_view_line(view, old_lineno - view->offset);
+ draw_view_line(view, view->lineno - view->offset);
+ wnoutrefresh(view->win);
+ }
} else {
view->ops->select(view, &view->line[view->lineno]);
}
return FALSE;
}
- if (view->p_lineno >= view->lines) {
- view->p_lineno = view->lines > 0 ? view->lines - 1 : 0;
- if (view->p_offset >= view->p_lineno) {
- unsigned long half = view->height / 2;
-
- if (view->p_lineno > half)
- view->p_offset = view->p_lineno - half;
- else
- view->p_offset = 0;
- }
- }
-
- if (view_is_displayed(view) &&
- view->offset != view->p_offset &&
- view->lineno != view->p_lineno)
+ if (goto_view_line(view, view->p_offset, view->p_lineno) &&
+ view_is_displayed(view))
werase(view->win);
- view->offset = view->p_offset;
view->yoffset = view->p_yoffset;
- view->lineno = view->p_lineno;
view->p_restore = FALSE;
return TRUE;
return TRUE;
if (!io_can_read(view->pipe)) {
- if (view->lines == 0) {
+ if (view->lines == 0 && view_is_displayed(view)) {
time_t secs = time(NULL) - view->start_time;
if (secs > 1 && secs > view->update_secs) {
{
int i;
- if (request == REQ_NONE) {
- doupdate();
+ if (request == REQ_NONE)
return TRUE;
- }
if (request > REQ_NONE) {
open_run_request(request);
case REQ_MAXIMIZE:
if (displayed_views() == 2)
- open_view(view, VIEW_REQ(view), OPEN_DEFAULT);
+ maximize_view(view);
break;
case REQ_TOGGLE_LINENO:
* followed. */
if (view->parent &&
view->parent->parent != view->parent) {
- memset(display, 0, sizeof(display));
- current_view = 0;
- display[current_view] = view->parent;
+ maximize_view(view->parent);
view->parent = view;
- resize_display();
- redraw_display(FALSE);
- report("");
break;
}
/* Fall-through */
}
static bool
-select_commit_parent(const char *id, char rev[SIZEOF_REV])
+select_commit_parent(const char *id, char rev[SIZEOF_REV], const char *path)
{
char buf[SIZEOF_STR * 4];
const char *revlist_argv[] = {
- "git", "rev-list", "-1", "--parents", id, NULL
+ "git", "rev-list", "-1", "--parents", id, "--", path, NULL
};
int parents;
return FALSE;
} else if (parents == 0) {
- report("The selected commit has no parents");
+ if (path)
+ report("Path '%s' does not exist in the parent", path);
+ else
+ report("The selected commit has no parents");
return FALSE;
}
struct blame {
struct blame_commit *commit;
+ unsigned long lineno;
char text[1];
};
{
struct blame_commit *commit;
struct blame *blame;
- const char *pos = text + SIZEOF_REV - 1;
+ const char *pos = text + SIZEOF_REV - 2;
+ size_t orig_lineno = 0;
size_t lineno;
size_t group;
- if (strlen(text) <= SIZEOF_REV || *pos != ' ')
+ if (strlen(text) <= SIZEOF_REV || pos[1] != ' ')
return NULL;
- if (!parse_number(&pos, &lineno, 1, view->lines) ||
+ if (!parse_number(&pos, &orig_lineno, 1, 9999999) ||
+ !parse_number(&pos, &lineno, 1, view->lines) ||
!parse_number(&pos, &group, 1, view->lines - lineno + 1))
return NULL;
blame = line->data;
blame->commit = commit;
+ blame->lineno = orig_lineno + group - 1;
line->dirty = 1;
}
return FALSE;
} else {
- size_t linelen = string_expand_length(line, opt_tab_size);
+ size_t linelen = strlen(line);
struct blame *blame = malloc(sizeof(*blame) + linelen);
if (!blame)
return FALSE;
blame->commit = NULL;
- string_expand(blame->text, linelen + 1, line, opt_tab_size);
+ strncpy(blame->text, line, linelen);
+ blame->text[linelen] = 0;
return add_line_data(view, blame, LINE_BLAME_ID) != NULL;
}
}
struct blame *blame = line->data;
struct tm *time = NULL;
const char *id = NULL, *author = NULL;
+ char text[SIZEOF_STR];
if (blame->commit && *blame->commit->filename) {
id = blame->commit->id;
if (draw_lineno(view, lineno))
return TRUE;
- draw_text(view, LINE_DEFAULT, blame->text, TRUE);
+ string_expand(text, sizeof(text), blame->text, opt_tab_size);
+ draw_text(view, LINE_DEFAULT, text, TRUE);
return TRUE;
}
static bool
-check_blame_commit(struct blame *blame)
+check_blame_commit(struct blame *blame, bool check_null_id)
{
if (!blame->commit)
report("Commit data not loaded yet");
- else if (!strcmp(blame->commit->id, NULL_ID))
+ else if (check_null_id && !strcmp(blame->commit->id, NULL_ID))
report("No commit exist for the selected line");
else
return TRUE;
return FALSE;
}
+static void
+setup_blame_parent_line(struct view *view, struct blame *blame)
+{
+ const char *diff_tree_argv[] = {
+ "git", "diff-tree", "-U0", blame->commit->id,
+ "--", blame->commit->filename, NULL
+ };
+ struct io io = {};
+ int parent_lineno = -1;
+ int blamed_lineno = -1;
+ char *line;
+
+ if (!run_io(&io, diff_tree_argv, NULL, IO_RD))
+ return;
+
+ while ((line = io_get(&io, '\n', TRUE))) {
+ if (*line == '@') {
+ char *pos = strchr(line, '+');
+
+ parent_lineno = atoi(line + 4);
+ if (pos)
+ blamed_lineno = atoi(pos + 1);
+
+ } else if (*line == '+' && parent_lineno != -1) {
+ if (blame->lineno == blamed_lineno - 1 &&
+ !strcmp(blame->text, line + 1)) {
+ view->lineno = parent_lineno ? parent_lineno - 1 : 0;
+ break;
+ }
+ blamed_lineno++;
+ }
+ }
+
+ done_io(&io);
+}
+
static enum request
blame_request(struct view *view, enum request request, struct line *line)
{
switch (request) {
case REQ_VIEW_BLAME:
- if (check_blame_commit(blame)) {
+ if (check_blame_commit(blame, TRUE)) {
string_copy(opt_ref, blame->commit->id);
+ string_copy(opt_file, blame->commit->filename);
+ if (blame->lineno)
+ view->lineno = blame->lineno;
open_view(view, REQ_VIEW_BLAME, OPEN_REFRESH);
}
break;
case REQ_PARENT:
- if (check_blame_commit(blame) &&
- select_commit_parent(blame->commit->id, opt_ref))
+ if (check_blame_commit(blame, TRUE) &&
+ select_commit_parent(blame->commit->id, opt_ref,
+ blame->commit->filename)) {
+ string_copy(opt_file, blame->commit->filename);
+ setup_blame_parent_line(view, blame);
open_view(view, REQ_VIEW_BLAME, OPEN_REFRESH);
+ }
break;
case REQ_ENTER:
- if (!blame->commit) {
- report("No commit loaded yet");
+ if (!check_blame_commit(blame, FALSE))
break;
- }
if (view_is_displayed(VIEW(REQ_VIEW_DIFF)) &&
!strcmp(blame->commit->id, VIEW(REQ_VIEW_DIFF)->ref))
return TRUE;
}
+static enum request
+status_load_error(struct view *view, struct view *stage, const char *path)
+{
+ if (displayed_views() == 2 || display[current_view] != view)
+ maximize_view(view);
+ report("Failed to load '%s': %s", path, io_strerror(&stage->io));
+ return REQ_NONE;
+}
+
static enum request
status_enter(struct view *view, struct line *line)
{
};
if (!prepare_update(stage, no_head_diff_argv, opt_cdup, FORMAT_DASH))
- return REQ_QUIT;
+ return status_load_error(view, stage, newpath);
} else {
const char *index_show_argv[] = {
"git", "diff-index", "--root", "--patch-with-stat",
};
if (!prepare_update(stage, index_show_argv, opt_cdup, FORMAT_DASH))
- return REQ_QUIT;
+ return status_load_error(view, stage, newpath);
}
if (status)
};
if (!prepare_update(stage, files_show_argv, opt_cdup, FORMAT_DASH))
- return REQ_QUIT;
+ return status_load_error(view, stage, newpath);
if (status)
info = "Unstaged changes to %s";
else
}
if (!prepare_update_file(stage, newpath))
- return REQ_QUIT;
+ return status_load_error(view, stage, newpath);
info = "Untracked file %s";
break;
return FALSE;
result = status_update_write(&io, status, type);
- done_io(&io);
- return result;
+ return done_io(&io) && result;
}
static bool
status_update_files(struct view *view, struct line *line)
{
+ char buf[sizeof(view->ref)];
struct io io = {};
bool result = TRUE;
struct line *pos = view->line + view->lines;
int files = 0;
int file, done;
+ int cursor_y, cursor_x;
if (!status_update_prepare(&io, line->type))
return FALSE;
for (pos = line; pos < view->line + view->lines && pos->data; pos++)
files++;
- for (file = 0, done = 0; result && file < files; line++, file++) {
+ string_copy(buf, view->ref);
+ getsyx(cursor_y, cursor_x);
+ for (file = 0, done = 5; result && file < files; line++, file++) {
int almost_done = file * 100 / files;
if (almost_done > done) {
string_format(view->ref, "updating file %u of %u (%d%% done)",
file, files, done);
update_view_title(view);
+ setsyx(cursor_y, cursor_x);
+ doupdate();
}
result = status_update_write(&io, line->data, line->type);
}
+ string_copy(view->ref, buf);
- done_io(&io);
- return result;
+ return done_io(&io) && result;
}
static bool
/* After returning the status view has been split to
* show the stage view. No further reloading is
* necessary. */
- status_enter(view, line);
- return REQ_NONE;
+ return status_enter(view, line);
case REQ_REFRESH:
/* Simply reload the view. */
load_refs(void)
{
static const char *ls_remote_argv[SIZEOF_ARG] = {
- "git", "ls-remote", ".", NULL
+ "git", "ls-remote", opt_git_dir, NULL
};
static bool init = FALSE;
return run_io_load(ls_remote_argv, "\t", read_ref);
}
+static void
+set_remote_branch(const char *name, const char *value, size_t valuelen)
+{
+ if (!strcmp(name, ".remote")) {
+ string_ncopy(opt_remote, value, valuelen);
+
+ } else if (*opt_remote && !strcmp(name, ".merge")) {
+ size_t from = strlen(opt_remote);
+
+ if (!prefixcmp(value, "refs/heads/"))
+ value += STRING_SIZE("refs/heads/");
+
+ if (!string_format_from(opt_remote, &from, "/%s", value))
+ opt_remote[0] = 0;
+ }
+}
+
static void
set_repo_config_option(char *name, char *value, int (*cmd)(int, const char **))
{
@@ -6645,16 +6736,57 @@ set_repo_config_option(char *name, char *value, int (*cmd)(int, const char **))
warn("Option 'tig.%s': %s", name, config_msg);
}
+static bool
+set_environment_variable(const char *name, const char *value)
+{
+ size_t len = strlen(name) + 1 + strlen(value) + 1;
+ char *env = malloc(len);
+
+ if (env &&
+ string_nformat(env, len, NULL, "%s=%s", name, value) &&
+ putenv(env) == 0)
+ return TRUE;
+ free(env);
+ return FALSE;
+}
+
+static void
+set_work_tree(const char *value)
+{
+ char cwd[SIZEOF_STR];
+
+ if (!getcwd(cwd, sizeof(cwd)))
+ die("Failed to get cwd path: %s", strerror(errno));
+ if (chdir(opt_git_dir) < 0)
+ die("Failed to chdir(%s): %s", strerror(errno));
+ if (!getcwd(opt_git_dir, sizeof(opt_git_dir)))
+ die("Failed to get git path: %s", strerror(errno));
+ if (chdir(cwd) < 0)
+ die("Failed to chdir(%s): %s", cwd, strerror(errno));
+ if (chdir(value) < 0)
+ die("Failed to chdir(%s): %s", value, strerror(errno));
+ if (!getcwd(cwd, sizeof(cwd)))
+ die("Failed to get cwd path: %s", strerror(errno));
+ if (!set_environment_variable("GIT_WORK_TREE", cwd))
+ die("Failed to set GIT_WORK_TREE to '%s'", cwd);
+ if (!set_environment_variable("GIT_DIR", opt_git_dir))
+ die("Failed to set GIT_DIR to '%s'", opt_git_dir);
+ opt_is_inside_work_tree = TRUE;
+}
+
static int
read_repo_config_option(char *name, size_t namelen, char *value, size_t valuelen)
{
if (!strcmp(name, "i18n.commitencoding"))
string_ncopy(opt_encoding, value, valuelen);
- if (!strcmp(name, "core.editor"))
+ else if (!strcmp(name, "core.editor"))
string_ncopy(opt_editor, value, valuelen);
- if (!prefixcmp(name, "tig.color."))
+ else if (!strcmp(name, "core.worktree"))
+ set_work_tree(value);
+
+ else if (!prefixcmp(name, "tig.color."))
set_repo_config_option(name + 10, value, option_color_command);
else if (!prefixcmp(name, "tig.bind."))
@@ -6663,27 +6795,9 @@ read_repo_config_option(char *name, size_t namelen, char *value, size_t valuelen
else if (!prefixcmp(name, "tig."))
set_repo_config_option(name + 4, value, option_set_command);
- /* 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 (!prefixcmp(value, "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;
- }
+ else if (*opt_head && !prefixcmp(name, "branch.") &&
+ !strncmp(name + 7, opt_head, strlen(opt_head)))
+ set_remote_branch(name + 7 + strlen(opt_head), value, valuelen);
return OK;
}
size_t i;
signal(SIGINT, quit);
+ signal(SIGPIPE, SIG_IGN);
if (setlocale(LC_ALL, "")) {
char *codeset = nl_langinfo(CODESET);
{
char *cmd = read_prompt(":");
- if (cmd) {
+ if (cmd && isdigit(*cmd)) {
+ int lineno = view->lineno + 1;
+
+ if (parse_int(&lineno, cmd, 1, view->lines + 1) == OK) {
+ select_view_line(view, lineno - 1);
+ report("");
+ } else {
+ report("Unable to parse '%s' as a line number", cmd);
+ }
+
+ } else if (cmd) {
struct view *next = VIEW(REQ_VIEW_PAGER);
const char *argv[SIZEOF_ARG] = { "git" };
int argc = 1;