Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions doc/tigrc.5.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,9 @@ bind generic G none
# User-defined external command to amend the last commit
bind status + !git commit --amend

# Toggle amend mode in the status or stage view
bind status a status-amend

# User-defined internal command that reloads ~/.tigrc
bind generic S :source ~/.tigrc

Expand Down Expand Up @@ -922,6 +925,7 @@ View-specific actions
[frame="none",grid="none",cols="25<m,75<"]
|=============================================================================
|status-update |Stage/unstage chunk or file changes
|status-amend |Toggle amend mode
|status-revert |Revert chunk or file changes
|status-merge |Merge file using external tool
|stage-update-line |Stage/unstage single line
Expand Down
6 changes: 5 additions & 1 deletion include/tig/repo.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ typedef char repo_str[SIZEOF_STR];
_(repo_str, git_dir) \
_(repo_str, worktree) \
_(repo_str, exec_dir) \
_(bool, is_inside_work_tree)
_(bool, is_inside_work_tree) \
_(bool, amend_mode)

#define REPO_INFO_FIELDS(type, name) type name;

Expand All @@ -51,6 +52,9 @@ struct index_diff {

bool index_diff(struct index_diff *diff, bool untracked, bool count_all);
bool update_index(void);
const char *repo_staged_parent(void);
bool repo_amend_mode_enabled(void);
bool repo_toggle_amend_mode(void);

#endif
/* vim: set ts=8 sw=8 noexpandtab: */
1 change: 1 addition & 0 deletions include/tig/request.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
\
REQ_GROUP("View-specific actions") \
REQ_(STATUS_UPDATE, "Stage/unstage chunk or file changes"), \
REQ_(STATUS_AMEND, "Toggle amend mode"), \
REQ_(STATUS_REVERT, "Revert chunk or file changes"), \
REQ_(STATUS_MERGE, "Merge file using external tool"), \
REQ_(STAGE_UPDATE_LINE, "Stage/unstage single line"), \
Expand Down
3 changes: 2 additions & 1 deletion src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,8 @@ main_add_changes(struct view *view, struct main_state *state, const char *parent

return main_add_changes_commit(view, LINE_STAT_UNTRACKED, untracked_parent, "Untracked changes")
&& main_add_changes_commit(view, LINE_STAT_UNSTAGED, unstaged_parent, "Unstaged changes")
&& main_add_changes_commit(view, LINE_STAT_STAGED, staged_parent, "Staged changes");
&& main_add_changes_commit(view, LINE_STAT_STAGED, staged_parent,
repo_amend_mode_enabled() ? "Amend changes" : "Staged changes");
}

static bool
Expand Down
42 changes: 42 additions & 0 deletions src/prompt.c
Original file line number Diff line number Diff line change
Expand Up @@ -1125,6 +1125,31 @@ run_prompt_command(struct view *view, const char *argv[])
return REQ_NONE;
}

static bool
run_request_should_amend(const char **argv)
{
return repo_amend_mode_enabled() &&
argv && argv[0] && argv[1] &&
!strcmp(argv[0], "git") &&
!strcmp(argv[1], "commit") &&
!argv_contains(argv, "--amend");
}

static bool
run_request_add_amend(const char ***dst_argv, const char **src_argv)
{
size_t i;

for (i = 0; src_argv[i]; i++) {
if (!argv_append(dst_argv, src_argv[i]))
return false;
if (i == 1 && !argv_append(dst_argv, "--amend"))
return false;
}

return true;
}

enum request
exec_run_request(struct view *view, struct run_request *req)
{
Expand All @@ -1146,6 +1171,23 @@ exec_run_request(struct view *view, struct run_request *req)
return REQ_NONE;
}

if (run_request_should_amend(argv)) {
const char **amend_argv = NULL;

if (!run_request_add_amend(&amend_argv, argv)) {
argv_free(argv);
free(argv);
argv_free(amend_argv);
free(amend_argv);
report("Failed to prepare amend commit command");
return REQ_NONE;
}

argv_free(argv);
free(argv);
argv = amend_argv;
}

if (req->flags.internal) {
request = run_prompt_command(view, argv);

Expand Down
63 changes: 63 additions & 0 deletions src/repo.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
#include "tig/io.h"
#include "tig/refdb.h"
#include "tig/git.h"
#include "tig/display.h"
#include "tig/status.h"

#define REPO_INFO_GIT_DIR "--git-dir"
#define REPO_INFO_WORK_TREE "--is-inside-work-tree"
Expand Down Expand Up @@ -131,6 +133,31 @@ load_repo_head(void)

struct repo_info repo;

const char *
repo_staged_parent(void)
{
return repo.amend_mode ? "HEAD^" : "HEAD";
}

bool
repo_amend_mode_enabled(void)
{
return repo.amend_mode;
}

bool
repo_toggle_amend_mode(void)
{
if (!repo.amend_mode && is_initial_commit()) {
report("Amend mode requires an existing commit");
return false;
}

repo.amend_mode = !repo.amend_mode;
report("Amend mode %s", repo.amend_mode ? "enabled" : "disabled");
return true;
}

/*
* Git index utils.
*/
Expand All @@ -154,6 +181,7 @@ index_diff(struct index_diff *diff, bool untracked, bool count_all)
const char *status_argv[] = {
"git", "status", "--porcelain", "-z", untracked_arg, NULL
};
const char *staged_parent = repo_staged_parent();
struct io io;
struct buffer buf;
bool ok = true;
Expand Down Expand Up @@ -185,6 +213,41 @@ index_diff(struct index_diff *diff, bool untracked, bool count_all)
ok = false;

io_done(&io);
if (!ok || !repo.amend_mode || is_initial_commit())
return ok;

{
const char *diff_index_argv[] = {
"git", "diff-index", "--cached", "--diff-filter=ACDMRTXB",
"-z", staged_parent, "--", NULL
};
int staged = 0;
struct status parsed = {0};

if (!io_run(&io, IO_RD, repo.exec_dir, NULL, diff_index_argv))
return false;

while (io_get(&io, &buf, 0, true) && (ok = buf.size > 3)) {
if (!status_get_diff(&parsed, buf.data, buf.size)) {
ok = false;
break;
}
staged++;
if (parsed.status == 'R' || parsed.status == 'C')
io_get(&io, &buf, 0, true);
if (!io_get(&io, &buf, 0, true))
break;
if (!count_all && staged && diff->unstaged &&
(!untracked || diff->untracked))
break;
}

if (io_error(&io))
ok = false;
io_done(&io);
diff->staged = staged;
}

return ok;
}

Expand Down
29 changes: 22 additions & 7 deletions src/stage.c
Original file line number Diff line number Diff line change
Expand Up @@ -452,11 +452,14 @@ find_deleted_line_in_head(struct view *view, struct line *line) {
unsigned long line_number_in_head, line_number = 0;
long bias_by_staged_changes = 0;
char buf[SIZEOF_STR] = "";
const char *staged_parent = repo_staged_parent();
char file_in_head_pathspec[sizeof("HEAD:") + SIZEOF_STR],
file_in_index_pathspec[sizeof(":") + SIZEOF_STR];
const char *file_in_head = NULL;
const char *ls_tree_argv[] = {
"git", "ls-tree", "-z", "HEAD", view->env->file, NULL
"git", "ls-tree", "-z",
stage_line_type == LINE_STAT_STAGED ? staged_parent : "HEAD",
view->env->file, NULL
};
const char *diff_argv[] = {
"git", "diff", file_in_head_pathspec, file_in_index_pathspec,
Expand All @@ -473,7 +476,9 @@ find_deleted_line_in_head(struct view *view, struct line *line) {
} else { // The file might might be renamed in the index. Find its old name.
const char *diff_index_argv[] = {
"git", "diff-index", "--cached", "-C",
"--diff-filter=ACR", "-z", "HEAD", NULL
"--diff-filter=ACR", "-z",
stage_line_type == LINE_STAT_STAGED ? staged_parent : "HEAD",
NULL
};
if (!io_run(&io, IO_RD, repo.exec_dir, NULL, diff_index_argv) || io.status)
return false;
Expand Down Expand Up @@ -565,6 +570,12 @@ stage_request(struct view *view, enum request request, struct line *line)
return REQ_NONE;
break;

case REQ_STATUS_AMEND:
if (!repo_toggle_amend_mode())
return REQ_NONE;
load_repo_head();
break;

case REQ_STATUS_REVERT:
if (!stage_revert(view, line))
return REQ_NONE;
Expand Down Expand Up @@ -655,7 +666,8 @@ stage_request(struct view *view, enum request request, struct line *line)

view->env->ref[0] = 0;
if (find_deleted_line_in_head(view, line))
string_copy(view->env->ref, "HEAD");
string_copy(view->env->ref,
stage_line_type == LINE_STAT_STAGED ? repo_staged_parent() : "HEAD");
else
view->env->goto_lineno = diff_get_lineno(view, line, false);
if (view->env->goto_lineno > 0)
Expand Down Expand Up @@ -698,7 +710,8 @@ stage_request(struct view *view, enum request request, struct line *line)
static void
stage_select(struct view *view, struct line *line)
{
const char *changes_msg = stage_line_type == LINE_STAT_STAGED ? "Staged changes"
const char *changes_msg = stage_line_type == LINE_STAT_STAGED
? repo_amend_mode_enabled() ? "Amend changes" : "Staged changes"
: stage_line_type == LINE_STAT_UNSTAGED ? "Unstaged changes"
: NULL;

Expand All @@ -713,9 +726,11 @@ stage_open(struct view *view, enum open_flags flags)
stage_status.new.name)
};
const char *index_show_argv[] = {
GIT_DIFF_STAGED(encoding_arg, diff_context_arg(), diff_prefix_arg(),
ignore_space_arg(), word_diff_arg(), stage_status.old.name,
stage_status.new.name)
"git", "diff-index", encoding_arg, "--textconv", "--patch-with-stat", "-C",
"--cached", "--diff-filter=ACDMRTXB", DIFF_ARGS, "%(cmdlineargs)",
diff_context_arg(), diff_prefix_arg(), ignore_space_arg(),
word_diff_arg(), repo_staged_parent(), "--",
stage_status.old.name, stage_status.new.name, NULL
};
const char *files_show_argv[] = {
GIT_DIFF_UNSTAGED(encoding_arg, diff_context_arg(), diff_prefix_arg(),
Expand Down
43 changes: 36 additions & 7 deletions src/status.c
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,6 @@ status_run(struct view *view, const char *argv[], char status, enum line_type ty
return true;
}

static const char *status_diff_index_argv[] = { GIT_DIFF_STAGED_FILES("-z") };
static const char *status_diff_files_argv[] = { GIT_DIFF_UNSTAGED_FILES("-z") };

static const char *status_list_other_argv[] = {
Expand Down Expand Up @@ -352,6 +351,14 @@ status_update_onbranch(void)
if (!string_format(status_onbranch, fmt,
prefix, head, tracking_info))
string_copy(status_onbranch, repo.head);
if (repo_amend_mode_enabled()) {
char amend_status[SIZEOF_STR];

if (string_nformat(amend_status, sizeof(amend_status), NULL,
"%s [amend]", status_onbranch)) {
string_copy(status_onbranch, amend_status);
}
}
return;
}

Expand Down Expand Up @@ -379,6 +386,10 @@ status_read_untracked(struct view *view)
static enum status_code
status_open(struct view *view, enum open_flags flags)
{
const char *status_diff_index_argv[] = {
"git", "diff-index", "-z", "%(cmdlineargs)", "--diff-filter=ACDMRTXB",
"-C", "--cached", repo_staged_parent(), "--", NULL
};
const char **staged_argv = is_initial_commit() ?
status_list_no_head_argv : status_diff_index_argv;
char staged_status = staged_argv == status_list_no_head_argv ? 'A' : 0;
Expand Down Expand Up @@ -423,7 +434,9 @@ status_get_column_data(struct view *view, const struct line *line, struct view_c
switch (line->type) {
case LINE_STAT_STAGED:
type = LINE_SECTION;
text = "Changes to be committed:";
text = repo_amend_mode_enabled() ?
"Changes to be amended:" :
"Changes to be committed:";
break;

case LINE_STAT_UNSTAGED:
Expand Down Expand Up @@ -722,6 +735,12 @@ status_request(struct view *view, enum request request, struct line *line)
return REQ_NONE;
break;

case REQ_STATUS_AMEND:
if (!repo_toggle_amend_mode())
return REQ_NONE;
load_repo_head();
break;

case REQ_STATUS_REVERT:
if (!status_revert(status, line->type, status_has_none(view, line)))
return REQ_NONE;
Expand Down Expand Up @@ -791,9 +810,13 @@ status_stage_info_(char *buf, size_t bufsize,
switch (type) {
case LINE_STAT_STAGED:
if (status && status->status)
info = "Staged changes to %s";
info = repo_amend_mode_enabled() ?
"Amend changes to %s" :
"Staged changes to %s";
else
info = "Staged changes";
info = repo_amend_mode_enabled() ?
"Amend changes" :
"Staged changes";
break;

case LINE_STAT_UNSTAGED:
Expand Down Expand Up @@ -831,15 +854,21 @@ status_select(struct view *view, struct line *line)

switch (line->type) {
case LINE_STAT_STAGED:
text = "Press %s to unstage %s for commit";
text = repo_amend_mode_enabled() ?
"Press %s to unstage %s from amend commit" :
"Press %s to unstage %s for commit";
break;

case LINE_STAT_UNSTAGED:
text = "Press %s to stage %s for commit";
text = repo_amend_mode_enabled() ?
"Press %s to stage %s for amend commit" :
"Press %s to stage %s for commit";
break;

case LINE_STAT_UNTRACKED:
text = "Press %s to stage %s for addition";
text = repo_amend_mode_enabled() ?
"Press %s to stage %s for amend commit" :
"Press %s to stage %s for addition";
break;

default:
Expand Down
Loading