added git stash command and pane

ever messed up a feature and its not working, and you just dont want to work on that shit anymore, well thats how i use stash. all the changes into the stash so that i can fix it in the future (will never get worked on in the future)
This commit is contained in:
2025-06-07 00:59:44 +02:00
parent ca2ed6f7bb
commit f505bb8dbb
7 changed files with 295 additions and 100 deletions

View File

@@ -256,29 +256,29 @@ int check_branch_divergence(int *commits_ahead, int *commits_behind) {
if (!commits_ahead || !commits_behind) {
return 0;
}
*commits_ahead = 0;
*commits_behind = 0;
// Check if we have a remote tracking branch
FILE *fp = popen("git rev-parse --abbrev-ref @{u} 2>/dev/null", "r");
if (!fp) {
return 0;
}
char remote_branch[256];
if (fgets(remote_branch, sizeof(remote_branch), fp) == NULL) {
pclose(fp);
return 0; // No remote tracking branch
}
pclose(fp);
// Remove newline
char *newline = strchr(remote_branch, '\n');
if (newline) {
*newline = '\0';
}
// Get commits ahead (local commits not in remote)
fp = popen("git rev-list --count @{u}..HEAD 2>/dev/null", "r");
if (fp) {
@@ -288,7 +288,7 @@ int check_branch_divergence(int *commits_ahead, int *commits_behind) {
}
pclose(fp);
}
// Get commits behind (remote commits not in local)
fp = popen("git rev-list --count HEAD..@{u} 2>/dev/null", "r");
if (fp) {
@@ -298,7 +298,64 @@ int check_branch_divergence(int *commits_ahead, int *commits_behind) {
}
pclose(fp);
}
// Branch has diverged if we have commits both ahead and behind
return (*commits_ahead > 0 && *commits_behind > 0) ? 1 : 0;
}
int create_git_stash(void) {
FILE *fp = popen("git status --porcelain 2>/dev/null", "r");
if (!fp)
return 0;
char line[256];
int has_changes = 0;
if (fgets(line, sizeof(line), fp) != NULL) {
has_changes = 1;
}
pclose(fp);
if (!has_changes) {
return 0;
}
time_t now = time(NULL);
struct tm *timeinfo = localtime(&now);
char cmd[512];
snprintf(cmd, sizeof(cmd), "git stash push -m \"WIP: stashed at %04d-%02d-%02d %02d:%02d:%02d\" 2>/dev/null >/dev/null",
timeinfo->tm_year + 1900, timeinfo->tm_mon + 1, timeinfo->tm_mday,
timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec);
int result = system(cmd);
return (result == 0) ? 1 : 0;
}
int get_git_stashes(char stashes[][512], int max_stashes) {
if (!stashes || max_stashes <= 0) {
return 0;
}
FILE *fp = popen("git stash list --format=\"%gd: %gs\" 2>/dev/null", "r");
if (!fp) {
return 0;
}
int count = 0;
char line[512];
while (fgets(line, sizeof(line), fp) != NULL && count < max_stashes) {
char *newline = strchr(line, '\n');
if (newline) {
*newline = '\0';
}
strncpy(stashes[count], line, 511);
stashes[count][511] = '\0';
count++;
}
pclose(fp);
return count;
}

View File

@@ -51,9 +51,13 @@ char *get_git_status(void);
* Check if the current branch has diverged from its remote tracking branch
*
* @param commits_ahead Pointer to store number of commits ahead of remote
* @param commits_behind Pointer to store number of commits behind remote
* @param commits_behind Pointer to store number of commits behind remote
* @return 1 if diverged (both ahead and behind > 0), 0 otherwise
*/
int check_branch_divergence(int *commits_ahead, int *commits_behind);
int create_git_stash(void);
int get_git_stashes(char stashed[][512], int max_stashes);
#endif // GIT_INTEGRATION_H

Binary file not shown.

View File

@@ -62,19 +62,23 @@ int init_ncurses_diff_viewer(NCursesDiffViewer *viewer) {
int available_height =
viewer->terminal_height - 1 -
viewer->status_bar_height; // Subtract top bar and status bar
viewer->file_panel_height = available_height * 0.6; // 60% of available height
viewer->commit_panel_height =
available_height - viewer->file_panel_height - 1; // Rest of height
viewer->file_panel_height = available_height * 0.4;
viewer->commit_panel_height = available_height * 0.4;
viewer->stash_panel_height = available_height - viewer->file_panel_height -
viewer->commit_panel_height - 2;
// Position status bar right after the main content
int status_bar_y = 1 + available_height;
// Create four windows
// Create five windows
viewer->file_list_win =
newwin(viewer->file_panel_height, viewer->file_panel_width, 1, 0);
viewer->commit_list_win =
newwin(viewer->commit_panel_height, viewer->file_panel_width,
1 + viewer->file_panel_height + 1, 0);
viewer->stash_list_win = newwin(
viewer->stash_panel_height, viewer->file_panel_width,
1 + viewer->file_panel_height + 1 + viewer->commit_panel_height + 1, 0);
viewer->file_content_win = newwin(
available_height, viewer->terminal_width - viewer->file_panel_width - 1,
1, viewer->file_panel_width + 1);
@@ -82,12 +86,13 @@ int init_ncurses_diff_viewer(NCursesDiffViewer *viewer) {
viewer->terminal_width, status_bar_y, 0);
if (!viewer->file_list_win || !viewer->file_content_win ||
!viewer->commit_list_win || !viewer->status_bar_win) {
cleanup_ncurses_diff_viewer(viewer);
return 0;
}
!viewer->commit_list_win || !viewer->stash_list_win ||
!viewer->status_bar_win) {
cleanup_ncurses_diff_viewer(viewer);
return 0;
}
return 1;
return 1;
}
/**
@@ -514,29 +519,30 @@ int show_diverged_branch_dialog(int commits_ahead, int commits_behind) {
// Draw dialog
wattron(dialog_win, COLOR_PAIR(3)); // Red for warning
box(dialog_win, 0, 0);
// Title
mvwprintw(dialog_win, 1, 2, "Branch has diverged!");
// Message
mvwprintw(dialog_win, 3, 2, "Local: %d commit(s) ahead", commits_ahead);
mvwprintw(dialog_win, 4, 2, "Remote: %d commit(s) ahead", commits_behind);
// Instructions
mvwprintw(dialog_win, 6, 2, "Force push anyway? (y/N):");
wattroff(dialog_win, COLOR_PAIR(3));
wrefresh(dialog_win);
// Get user input
int ch;
int result = 0;
while ((ch = wgetch(dialog_win)) != ERR) {
if (ch == 'y' || ch == 'Y') {
result = 1;
break;
} else if (ch == 'n' || ch == 'N' || ch == 27 || ch == 'q') { // ESC or q or n
} else if (ch == 'n' || ch == 'N' || ch == 27 ||
ch == 'q') { // ESC or q or n
result = 0;
break;
} else if (ch == '\n' || ch == '\r') {
@@ -552,7 +558,7 @@ int show_diverged_branch_dialog(int commits_ahead, int commits_behind) {
wrefresh(saved_screen);
delwin(saved_screen);
}
return result;
}
@@ -580,35 +586,35 @@ int show_reset_confirmation_dialog(void) {
char input_buffer[10] = "";
int input_pos = 0;
while (1) {
// Clear and redraw dialog
werase(dialog_win);
wattron(dialog_win, COLOR_PAIR(3)); // Red for warning
box(dialog_win, 0, 0);
// Title
mvwprintw(dialog_win, 1, 2, "HARD RESET WARNING!");
// Warning message
mvwprintw(dialog_win, 3, 2, "This will permanently delete the most recent");
mvwprintw(dialog_win, 4, 2, "commit and ALL uncommitted changes!");
// Instructions
mvwprintw(dialog_win, 6, 2, "Type 'yes' to confirm or ESC to cancel:");
// Input field
mvwprintw(dialog_win, 7, 2, "> %s", input_buffer);
wattroff(dialog_win, COLOR_PAIR(3));
wrefresh(dialog_win);
// Position cursor
wmove(dialog_win, 7, 4 + strlen(input_buffer));
// Get user input
int ch = wgetch(dialog_win);
if (ch == 27 || ch == 'q') { // ESC or q
break;
} else if (ch == '\n' || ch == '\r') {
@@ -648,7 +654,7 @@ int show_reset_confirmation_dialog(void) {
wrefresh(saved_screen);
delwin(saved_screen);
}
return 0;
}
@@ -1197,7 +1203,7 @@ int push_commit(NCursesDiffViewer *viewer, int commit_index) {
int commits_ahead = 0;
int commits_behind = 0;
int is_diverged = check_branch_divergence(&commits_ahead, &commits_behind);
// If diverged, show confirmation dialog
if (is_diverged) {
if (!show_diverged_branch_dialog(commits_ahead, commits_behind)) {
@@ -1220,7 +1226,8 @@ int push_commit(NCursesDiffViewer *viewer, int commit_index) {
int result;
if (is_diverged) {
// Use force push with lease for safety
result = system("git push --force-with-lease origin 2>/dev/null >/dev/null");
result =
system("git push --force-with-lease origin 2>/dev/null >/dev/null");
} else {
// Normal push
result = system("git push origin 2>/dev/null >/dev/null");
@@ -1231,7 +1238,7 @@ int push_commit(NCursesDiffViewer *viewer, int commit_index) {
viewer->sync_status = SYNC_STATUS_PUSHED_APPEARING;
viewer->animation_frame = 0;
viewer->text_char_count = 0;
// Refresh commit history to get proper push status
get_commit_history(viewer);
return 1;
@@ -1465,7 +1472,6 @@ void render_commit_list_window(NCursesDiffViewer *viewer) {
wrefresh(viewer->commit_list_win);
}
/**
* Render the file content window
*/
@@ -1580,7 +1586,7 @@ void render_status_bar(NCursesDiffViewer *viewer) {
// Left side: Key bindings based on current mode
char keybindings[256] = "";
if (viewer->current_mode == NCURSES_MODE_FILE_LIST) {
strcpy(keybindings, "Stage: <space> | Stage All: a | Commit: c");
strcpy(keybindings, "Stage: <space> | Stage All: a | Stash: s | Commit: c");
} else if (viewer->current_mode == NCURSES_MODE_COMMIT_LIST) {
strcpy(keybindings, "Push: P | Pull: p | Reset: r/R | Amend: a | Nav: j/k");
} else if (viewer->current_mode == NCURSES_MODE_FILE_VIEW) {
@@ -2025,6 +2031,11 @@ int handle_ncurses_diff_input(NCursesDiffViewer *viewer, int key) {
mark_all_files(viewer);
break;
case 's':
case 'S':
create_ncurses_git_stash(viewer);
break;
case 'c':
case 'C': // Commit marked files
{
@@ -2189,6 +2200,10 @@ int run_ncurses_diff_viewer(void) {
// Get changed files (can be 0, that's okay)
get_ncurses_changed_files(&viewer);
//get stashes
get_ncurses_git_stashes(&viewer);
// Load commit history
get_commit_history(&viewer);
@@ -2202,7 +2217,7 @@ int run_ncurses_diff_viewer(void) {
if (viewer.current_mode == NCURSES_MODE_FILE_LIST) {
mvprintw(0, 0,
"Git Diff Viewer: 1=files 2=view 3=commits | j/k=nav Space=mark "
"A=all C=commit P=push | q=quit");
"A=all S=stash C=commit P=push | q=quit");
} else if (viewer.current_mode == NCURSES_MODE_FILE_VIEW) {
mvprintw(0, 0,
"Git Diff Viewer: 1=files 2=view 3=commits | j/k=scroll "
@@ -2218,6 +2233,7 @@ int run_ncurses_diff_viewer(void) {
render_file_list_window(&viewer);
render_file_content_window(&viewer);
render_commit_list_window(&viewer);
render_stash_list_window(&viewer);
render_status_bar(&viewer);
// Main display loop
@@ -2235,7 +2251,7 @@ int run_ncurses_diff_viewer(void) {
if (viewer.current_mode == NCURSES_MODE_FILE_LIST) {
mvprintw(0, 0,
"Git Diff Viewer: 1=files 2=view 3=commits | j/k=nav "
"Space=mark A=all C=commit P=push | q=quit");
"Space=mark A=all S=stash C=commit P=push | q=quit");
} else if (viewer.current_mode == NCURSES_MODE_FILE_VIEW) {
mvprintw(0, 0,
"Git Diff Viewer: 1=files 2=view 3=commits | j/k=scroll "
@@ -2256,6 +2272,7 @@ int run_ncurses_diff_viewer(void) {
render_file_list_window(&viewer);
render_file_content_window(&viewer);
render_commit_list_window(&viewer);
render_stash_list_window(&viewer);
render_status_bar(&viewer);
// Keep cursor hidden
@@ -2274,6 +2291,102 @@ int run_ncurses_diff_viewer(void) {
return 0;
}
/**
* Get list of git stashes
*/
int get_ncurses_git_stashes(NCursesDiffViewer *viewer) {
if (!viewer) return 0;
char stash_lines[MAX_STASHES][512];
viewer->stash_count = get_git_stashes(stash_lines, MAX_STASHES);
for (int i = 0; i < viewer->stash_count; i++) {
strncpy(viewer->stashes[i].stash_info, stash_lines[i], 511);
viewer->stashes[i].stash_info[511] = '\0';
}
return viewer->stash_count;
}
/**
* Create a new git stash
*/
int create_ncurses_git_stash(NCursesDiffViewer *viewer) {
if (!viewer) return 0;
int result = create_git_stash();
if (result) {
// Refresh everything after creating stash
get_ncurses_changed_files(viewer);
get_ncurses_git_stashes(viewer);
get_commit_history(viewer);
// Reset file selection since changes are stashed
viewer->selected_file = 0;
viewer->file_line_count = 0;
viewer->file_scroll_offset = 0;
// Reload current file if any files still exist
if (viewer->file_count > 0 && viewer->selected_file < viewer->file_count) {
load_full_file_with_diff(viewer, viewer->files[viewer->selected_file].filename);
}
}
return result;
}
/**
* Render the stash list window
*/
void render_stash_list_window(NCursesDiffViewer *viewer) {
if (!viewer || !viewer->stash_list_win)
return;
// Clear the entire window first
werase(viewer->stash_list_win);
// Draw rounded border and title
draw_rounded_box(viewer->stash_list_win);
mvwprintw(viewer->stash_list_win, 0, 2, " 4. Stashes ");
int max_stashes_visible = viewer->stash_panel_height - 2;
// Clear the content area with spaces
for (int y = 1; y < viewer->stash_panel_height - 1; y++) {
for (int x = 1; x < viewer->file_panel_width - 1; x++) {
mvwaddch(viewer->stash_list_win, y, x, ' ');
}
}
if (viewer->stash_count == 0) {
// Show "No stashes" message
mvwprintw(viewer->stash_list_win, 1, 2, "No stashes available");
} else {
for (int i = 0; i < max_stashes_visible && i < viewer->stash_count; i++) {
int y = i + 1;
// Show stash info (truncated to fit panel)
int max_stash_len = viewer->file_panel_width - 4;
char truncated_stash[256];
if ((int)strlen(viewer->stashes[i].stash_info) > max_stash_len) {
strncpy(truncated_stash, viewer->stashes[i].stash_info, max_stash_len - 2);
truncated_stash[max_stash_len - 2] = '\0';
strcat(truncated_stash, "..");
} else {
strcpy(truncated_stash, viewer->stashes[i].stash_info);
}
// Color stash entries yellow
wattron(viewer->stash_list_win, COLOR_PAIR(4));
mvwprintw(viewer->stash_list_win, y, 2, "%s", truncated_stash);
wattroff(viewer->stash_list_win, COLOR_PAIR(4));
}
}
wrefresh(viewer->stash_list_win);
}
/**
* Clean up ncurses diff viewer resources
*/
@@ -2288,9 +2401,13 @@ void cleanup_ncurses_diff_viewer(NCursesDiffViewer *viewer) {
if (viewer->commit_list_win) {
delwin(viewer->commit_list_win);
}
if (viewer->stash_list_win) {
delwin(viewer->stash_list_win);
}
if (viewer->status_bar_win) {
delwin(viewer->status_bar_win);
}
}
endwin();
}

View File

@@ -15,80 +15,89 @@
#define MAX_COMMITS 50
#define MAX_COMMIT_TITLE_LEN 256
#define MAX_AUTHOR_INITIALS 3
#define MAX_STASHES 20
typedef struct {
char filename[MAX_FILENAME_LEN];
char status; // 'M' = modified, 'A' = added, 'D' = deleted
int marked_for_commit; // 1 if marked for commit, 0 otherwise
char stash_info[512];
} NCursesStash;
typedef struct {
char filename[MAX_FILENAME_LEN];
char status; // 'M' = modified, 'A' = added, 'D' = deleted
int marked_for_commit; // 1 if marked for commit, 0 otherwise
} NCursesChangedFile;
typedef struct {
char line[1024];
char type; // '+' = addition, '-' = deletion, ' ' = context, '@' = hunk header
int is_diff_line; // 1 if this is a diff line, 0 if original file line
char line[1024];
char type; // '+' = addition, '-' = deletion, ' ' = context, '@' = hunk header
int is_diff_line; // 1 if this is a diff line, 0 if original file line
} NCursesFileLine;
typedef struct {
char hash[16]; // Short commit hash
char author_initials[MAX_AUTHOR_INITIALS];
char title[MAX_COMMIT_TITLE_LEN];
int is_pushed; // 1 if pushed to remote, 0 if local only
char hash[16]; // Short commit hash
char author_initials[MAX_AUTHOR_INITIALS];
char title[MAX_COMMIT_TITLE_LEN];
int is_pushed; // 1 if pushed to remote, 0 if local only
} NCursesCommit;
typedef enum {
NCURSES_MODE_FILE_LIST,
NCURSES_MODE_FILE_VIEW,
NCURSES_MODE_COMMIT_LIST
NCURSES_MODE_FILE_LIST,
NCURSES_MODE_FILE_VIEW,
NCURSES_MODE_COMMIT_LIST
} NCursesViewMode;
typedef enum {
SYNC_STATUS_IDLE,
SYNC_STATUS_SYNCING_APPEARING,
SYNC_STATUS_SYNCING_VISIBLE,
SYNC_STATUS_SYNCING_DISAPPEARING,
SYNC_STATUS_PUSHING_APPEARING,
SYNC_STATUS_PUSHING_VISIBLE,
SYNC_STATUS_PUSHING_DISAPPEARING,
SYNC_STATUS_PULLING_APPEARING,
SYNC_STATUS_PULLING_VISIBLE,
SYNC_STATUS_PULLING_DISAPPEARING,
SYNC_STATUS_SYNCED_APPEARING,
SYNC_STATUS_SYNCED_VISIBLE,
SYNC_STATUS_SYNCED_DISAPPEARING,
SYNC_STATUS_PUSHED_APPEARING,
SYNC_STATUS_PUSHED_VISIBLE,
SYNC_STATUS_PUSHED_DISAPPEARING,
SYNC_STATUS_PULLED_APPEARING,
SYNC_STATUS_PULLED_VISIBLE,
SYNC_STATUS_PULLED_DISAPPEARING
SYNC_STATUS_IDLE,
SYNC_STATUS_SYNCING_APPEARING,
SYNC_STATUS_SYNCING_VISIBLE,
SYNC_STATUS_SYNCING_DISAPPEARING,
SYNC_STATUS_PUSHING_APPEARING,
SYNC_STATUS_PUSHING_VISIBLE,
SYNC_STATUS_PUSHING_DISAPPEARING,
SYNC_STATUS_PULLING_APPEARING,
SYNC_STATUS_PULLING_VISIBLE,
SYNC_STATUS_PULLING_DISAPPEARING,
SYNC_STATUS_SYNCED_APPEARING,
SYNC_STATUS_SYNCED_VISIBLE,
SYNC_STATUS_SYNCED_DISAPPEARING,
SYNC_STATUS_PUSHED_APPEARING,
SYNC_STATUS_PUSHED_VISIBLE,
SYNC_STATUS_PUSHED_DISAPPEARING,
SYNC_STATUS_PULLED_APPEARING,
SYNC_STATUS_PULLED_VISIBLE,
SYNC_STATUS_PULLED_DISAPPEARING
} SyncStatus;
typedef struct {
NCursesChangedFile files[MAX_FILES];
int file_count;
int selected_file;
NCursesFileLine file_lines[MAX_FULL_FILE_LINES];
int file_line_count;
int file_scroll_offset;
NCursesCommit commits[MAX_COMMITS];
int commit_count;
int selected_commit;
WINDOW *file_list_win;
WINDOW *file_content_win;
WINDOW *commit_list_win;
WINDOW *status_bar_win;
int terminal_width;
int terminal_height;
int file_panel_width;
int file_panel_height;
int commit_panel_height;
int status_bar_height;
NCursesViewMode current_mode;
SyncStatus sync_status;
int spinner_frame;
time_t last_sync_time;
int animation_frame;
int text_char_count;
NCursesChangedFile files[MAX_FILES];
int file_count;
int selected_file;
NCursesFileLine file_lines[MAX_FULL_FILE_LINES];
int file_line_count;
int file_scroll_offset;
NCursesCommit commits[MAX_COMMITS];
int commit_count;
int selected_commit;
NCursesStash stashes[MAX_STASHES];
int stash_count;
WINDOW *file_list_win;
WINDOW *file_content_win;
WINDOW *commit_list_win;
WINDOW *stash_list_win;
WINDOW *status_bar_win;
int terminal_width;
int terminal_height;
int file_panel_width;
int file_panel_height;
int commit_panel_height;
int stash_panel_height;
int status_bar_height;
NCursesViewMode current_mode;
SyncStatus sync_status;
int spinner_frame;
time_t last_sync_time;
int animation_frame;
int text_char_count;
} NCursesDiffViewer;
/**
@@ -159,7 +168,8 @@ void mark_all_files(NCursesDiffViewer *viewer);
/**
* Commit marked files with title and message
*/
int commit_marked_files(NCursesDiffViewer *viewer, const char *commit_title, const char *commit_message);
int commit_marked_files(NCursesDiffViewer *viewer, const char *commit_title,
const char *commit_message);
/**
* Push specific commit
@@ -179,7 +189,8 @@ void render_commit_list_window(NCursesDiffViewer *viewer);
/**
* Get commit title and message input from user
*/
int get_commit_title_input(char *title, int max_len, char *message, int max_message_len);
int get_commit_title_input(char *title, int max_len, char *message,
int max_message_len);
/**
* Draw a box with rounded corners
@@ -196,4 +207,10 @@ void render_status_bar(NCursesDiffViewer *viewer);
*/
void update_sync_status(NCursesDiffViewer *viewer);
#endif // NCURSES_DIFF_VIEWER_H
int get_ncurses_git_stashes(NCursesDiffViewer *viewer);
int create_ncurses_git_stash(NCursesDiffViewer *viewer);
void render_stash_list_window(NCursesDiffViewer *viewer);
#endif // NCURSES_DIFF_VIEWER_H

Binary file not shown.

BIN
shell

Binary file not shown.