index 507ecdbf5c6a3750e568a7505e1ef4614b3fb5d5..d2bc2dd46555008a03b6aa903fea78ea304ecfbf 100644 (file)
--- a/tig.c
+++ b/tig.c
#define prefixcmp(str1, str2) \
strncmp(str1, str2, STRING_SIZE(str2))
+static inline int
+suffixcmp(const char *str, int slen, const char *suffix)
+{
+ size_t len = slen >= 0 ? slen : strlen(str);
+ size_t suffixlen = strlen(suffix);
+
+ return suffixlen < len ? strcmp(str + len - suffixlen, suffix) : -1;
+}
+
/* Shell quoting
*
* NOTE: The following is a slightly modified copy of the git project's shell
static char opt_file[SIZEOF_STR] = "";
static char opt_ref[SIZEOF_REF] = "";
static char opt_head[SIZEOF_REF] = "";
+static char opt_head_rev[SIZEOF_REV] = "";
static char opt_remote[SIZEOF_REF] = "";
-static bool opt_no_head = TRUE;
static FILE *opt_pipe = NULL;
static char opt_encoding[20] = "UTF-8";
static bool opt_utf8 = TRUE;
static char opt_git_dir[SIZEOF_STR] = "";
static signed char opt_is_inside_work_tree = -1; /* set to TRUE or FALSE */
static char opt_editor[SIZEOF_STR] = "";
+static FILE *opt_tty = NULL;
+
+#define is_initial_commit() (!*opt_head_rev)
+#define is_head_commit(rev) (!strcmp((rev), "HEAD") || !strcmp(opt_head_rev, (rev)))
static enum request
parse_options(int argc, const char *argv[])
}
}
+ if (ferror(view->pipe) && errno != 0) {
+ report("Failed to read: %s", strerror(errno));
+ end_update(view, TRUE);
+
+ } else if (feof(view->pipe)) {
+ report("");
+ end_update(view, FALSE);
+ }
+
if (!view_is_displayed(view))
- goto check_pipe;
+ return TRUE;
if (view == VIEW(REQ_VIEW_TREE)) {
/* Clear the view and redraw everything since the tree sorting
/* Update the title _after_ the redraw so that if the redraw picks up a
* commit reference in view->ref it'll be available here. */
update_view_title(view);
-
-check_pipe:
- if (ferror(view->pipe) && errno != 0) {
- report("Failed to read: %s", strerror(errno));
- end_update(view, TRUE);
-
- } else if (feof(view->pipe)) {
- report("");
- end_update(view, FALSE);
- }
-
return TRUE;
alloc_error:
endwin(); /* restore original tty modes */
system(cmd);
fprintf(stderr, "Press Enter to continue");
- getc(stdin);
+ getc(opt_tty);
reset_prog_mode();
redraw_display();
}
{
enum open_flags flags;
- if (request == REQ_VIEW_BLAME) {
- const char *filename = tree_path(line);
-
- if (line->type == LINE_TREE_DIR) {
- report("Cannot show blame for directory %s", opt_path);
+ switch (request) {
+ case REQ_VIEW_BLAME:
+ if (line->type != LINE_TREE_FILE) {
+ report("Blame only supported for files");
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 ..' */
- request = REQ_ENTER;
- line = &view->line[1];
+
+ case REQ_EDIT:
+ if (line->type != LINE_TREE_FILE) {
+ report("Edit only supported for files");
+ } else if (!is_head_commit(view->vid)) {
+ report("Edit only supported for files in the current work tree");
} else {
+ open_editor(TRUE, opt_file);
+ }
+ return REQ_NONE;
+
+ case REQ_TREE_PARENT:
+ if (!*opt_path) {
/* quit view if at top of tree */
return REQ_VIEW_CLOSE;
}
- }
- if (request != REQ_ENTER)
+ /* fake 'cd ..' */
+ line = &view->line[1];
+ break;
+
+ case REQ_ENTER:
+ break;
+
+ default:
return request;
+ }
/* Cleanup the stack if the tree view is at a different tree. */
while (!*opt_path && tree_stack)
if (line->type == LINE_TREE_FILE) {
string_copy_rev(ref_blob, text);
+ string_format(opt_file, "%s%s", opt_path, tree_path(line));
} else if (line->type != LINE_TREE_DIR) {
return;
return FALSE;
add_line_data(view, NULL, LINE_STAT_HEAD);
- if (opt_no_head)
+ if (is_initial_commit())
string_copy(status_onbranch, "Initial commit");
else if (!*opt_head)
string_copy(status_onbranch, "Not currently on any branch");
system("git update-index -q --refresh >/dev/null 2>/dev/null");
- if (opt_no_head) {
+ if (is_initial_commit()) {
if (!status_run(view, STATUS_LIST_NO_HEAD_CMD, 'A', LINE_STAT_STAGED))
return FALSE;
} else if (!status_run(view, STATUS_DIFF_INDEX_CMD, 0, LINE_STAT_STAGED)) {
switch (line->type) {
case LINE_STAT_STAGED:
- if (opt_no_head) {
+ if (is_initial_commit()) {
if (!string_format_from(opt_cmd, &cmdsize,
STATUS_DIFF_NO_HEAD_SHOW_CMD,
newpath))
return REQ_NONE;
}
+ if (!suffixcmp(status->new.name, -1, "/")) {
+ report("Cannot display a directory");
+ return REQ_NONE;
+ }
+
opt_pipe = fopen(status->new.name, "r");
info = "Untracked file %s";
break;
char file_sq[SIZEOF_STR];
if (sq_quote(file_sq, 0, status->old.name) >= sizeof(file_sq) ||
- !string_format(cmd, "git checkout %s%s", opt_cdup, file_sq))
+ !string_format(cmd, "git checkout -- %s%s", opt_cdup, file_sq))
return FALSE;
return run_confirm(cmd, "Are you sure you want to overwrite any changes?");
case REQ_EDIT:
if (!status)
return request;
+ if (status->status == 'D') {
+ report("File has been deleted.");
+ return REQ_NONE;
+ }
open_editor(status->status != '?', status->new.name);
break;
{
struct line *chunk = NULL;
- if (!opt_no_head && stage_line_type != LINE_STAT_UNTRACKED)
+ if (!is_initial_commit() && stage_line_type != LINE_STAT_UNTRACKED)
chunk = stage_diff_find(view, line, LINE_DIFF_CHUNK);
if (chunk) {
{
struct line *chunk = NULL;
- if (!opt_no_head && stage_line_type == LINE_STAT_UNSTAGED)
+ if (!is_initial_commit() && stage_line_type == LINE_STAT_UNSTAGED)
chunk = stage_diff_find(view, line, LINE_DIFF_CHUNK);
if (chunk) {
case REQ_EDIT:
if (!stage_status.new.name[0])
return request;
+ if (stage_status.status == 'D') {
+ report("File has been deleted.");
+ return REQ_NONE;
+ }
open_editor(stage_status.status != '?', stage_status.new.name);
break;
if (!status_exists(&stage_status, stage_line_type))
return REQ_VIEW_CLOSE;
- if (stage_line_type == LINE_STAT_UNTRACKED)
+ if (stage_line_type == LINE_STAT_UNTRACKED) {
+ if (!suffixcmp(stage_status.new.name, -1, "/")) {
+ report("Cannot display a directory");
+ return REQ_NONE;
+ }
+
opt_pipe = fopen(stage_status.new.name, "r");
+ }
open_view(view, REQ_VIEW_STAGE, OPEN_REFRESH);
return REQ_NONE;
/* Initialize the curses library */
if (isatty(STDIN_FILENO)) {
cursed = !!initscr();
+ opt_tty = stdin;
} else {
/* Leave stdin and stdout alone when acting as a pager. */
- FILE *io = fopen("/dev/tty", "r+");
-
- if (!io)
+ opt_tty = fopen("/dev/tty", "r+");
+ if (!opt_tty)
die("Failed to open /dev/tty");
- cursed = !!newterm(NULL, io, io);
+ cursed = !!newterm(NULL, opt_tty, opt_tty);
}
if (!cursed)
bool head = FALSE;
if (!prefixcmp(name, "refs/tags/")) {
- if (!strcmp(name + namelen - 3, "^{}")) {
+ if (!suffixcmp(name, namelen, "^{}")) {
namelen -= 3;
name[namelen] = 0;
if (refs_size > 0 && refs[refs_size - 1].ltag == TRUE)
head = !strncmp(opt_head, name, namelen);
} else if (!strcmp(name, "HEAD")) {
- opt_no_head = FALSE;
+ string_ncopy(opt_head_rev, id, idlen);
return OK;
}