diff --git a/git_integration.c b/git_integration.c index dd164c9..5195cf4 100644 --- a/git_integration.c +++ b/git_integration.c @@ -386,3 +386,48 @@ int get_git_stashes(char stashes[][512], int max_stashes) { pclose(fp); return count; } + +/** + * Apply a git stash by index (keeps stash in list) + */ +int apply_git_stash(int stash_index) { + if (stash_index < 0) { + return 0; + } + + char cmd[128]; + snprintf(cmd, sizeof(cmd), "git stash apply stash@{%d} 2>/dev/null >/dev/null", stash_index); + + int result = system(cmd); + return (result == 0) ? 1 : 0; +} + +/** + * Pop a git stash by index (applies and removes stash from list) + */ +int pop_git_stash(int stash_index) { + if (stash_index < 0) { + return 0; + } + + char cmd[128]; + snprintf(cmd, sizeof(cmd), "git stash pop stash@{%d} 2>/dev/null >/dev/null", stash_index); + + int result = system(cmd); + return (result == 0) ? 1 : 0; +} + +/** + * Drop a git stash by index (removes stash without applying) + */ +int drop_git_stash(int stash_index) { + if (stash_index < 0) { + return 0; + } + + char cmd[128]; + snprintf(cmd, sizeof(cmd), "git stash drop stash@{%d} 2>/dev/null >/dev/null", stash_index); + + int result = system(cmd); + return (result == 0) ? 1 : 0; +} diff --git a/git_integration.h b/git_integration.h index 9c470b6..8494be8 100644 --- a/git_integration.h +++ b/git_integration.h @@ -62,4 +62,28 @@ int create_git_stash_with_name(const char *stash_name); int get_git_stashes(char stashed[][512], int max_stashes); +/** + * Apply a git stash by index (keeps stash in list) + * + * @param stash_index Index of stash to apply (0 = most recent) + * @return 1 if stash was applied successfully, 0 otherwise + */ +int apply_git_stash(int stash_index); + +/** + * Pop a git stash by index (applies and removes stash from list) + * + * @param stash_index Index of stash to pop (0 = most recent) + * @return 1 if stash was popped successfully, 0 otherwise + */ +int pop_git_stash(int stash_index); + +/** + * Drop a git stash by index (removes stash without applying) + * + * @param stash_index Index of stash to drop (0 = most recent) + * @return 1 if stash was dropped successfully, 0 otherwise + */ +int drop_git_stash(int stash_index); + #endif // GIT_INTEGRATION_H diff --git a/git_integration.o b/git_integration.o index bb4ae67..0b9f766 100644 Binary files a/git_integration.o and b/git_integration.o differ diff --git a/ncurses_diff_viewer.c b/ncurses_diff_viewer.c index f101f86..2a52e39 100644 --- a/ncurses_diff_viewer.c +++ b/ncurses_diff_viewer.c @@ -23,6 +23,7 @@ int init_ncurses_diff_viewer(NCursesDiffViewer *viewer) { memset(viewer, 0, sizeof(NCursesDiffViewer)); viewer->selected_file = 0; viewer->file_scroll_offset = 0; + viewer->selected_stash = 0; viewer->current_mode = NCURSES_MODE_FILE_LIST; viewer->sync_status = SYNC_STATUS_IDLE; viewer->spinner_frame = 0; @@ -1589,6 +1590,8 @@ void render_status_bar(NCursesDiffViewer *viewer) { strcpy(keybindings, "Stage: | 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_STASH_LIST) { + strcpy(keybindings, "Apply: | Pop: g | Drop: d | Nav: j/k"); } else if (viewer->current_mode == NCURSES_MODE_FILE_VIEW) { strcpy(keybindings, "Scroll: j/k | Page: Ctrl+U/D | Back: Esc"); } @@ -1987,6 +1990,9 @@ int handle_ncurses_diff_input(NCursesDiffViewer *viewer, int key) { case '3': viewer->current_mode = NCURSES_MODE_COMMIT_LIST; break; + case '4': + viewer->current_mode = NCURSES_MODE_STASH_LIST; + break; } if (viewer->current_mode == NCURSES_MODE_FILE_LIST) { @@ -2181,6 +2187,98 @@ int handle_ncurses_diff_input(NCursesDiffViewer *viewer, int key) { } break; } + } else if (viewer->current_mode == NCURSES_MODE_STASH_LIST) { + // Stash list mode navigation + switch (key) { + case 27: // ESC + case '\t': // Tab - return to file list mode + viewer->current_mode = NCURSES_MODE_FILE_LIST; + break; + + case KEY_UP: + case 'k': + if (viewer->selected_stash > 0) { + viewer->selected_stash--; + } + break; + + case KEY_DOWN: + case 'j': + if (viewer->selected_stash < viewer->stash_count - 1) { + viewer->selected_stash++; + } + break; + + case ' ': // Space - Apply stash (keeps stash in list) + if (viewer->stash_count > 0 && viewer->selected_stash < viewer->stash_count) { + if (apply_git_stash(viewer->selected_stash)) { + // Refresh everything after applying stash + get_ncurses_changed_files(viewer); + get_commit_history(viewer); + + // Reset file selection if no files remain + if (viewer->file_count == 0) { + viewer->selected_file = 0; + viewer->file_line_count = 0; + viewer->file_scroll_offset = 0; + } else if (viewer->selected_file >= viewer->file_count) { + viewer->selected_file = viewer->file_count - 1; + } + + // Reload current file if any files exist + if (viewer->file_count > 0 && viewer->selected_file < viewer->file_count) { + load_full_file_with_diff(viewer, viewer->files[viewer->selected_file].filename); + } + } + } + break; + + case 'g': + case 'G': // Pop stash (applies and removes from list) + if (viewer->stash_count > 0 && viewer->selected_stash < viewer->stash_count) { + if (pop_git_stash(viewer->selected_stash)) { + // Refresh everything after popping stash + get_ncurses_changed_files(viewer); + get_ncurses_git_stashes(viewer); + get_commit_history(viewer); + + // Adjust selected stash if needed + if (viewer->selected_stash >= viewer->stash_count && viewer->stash_count > 0) { + viewer->selected_stash = viewer->stash_count - 1; + } + + // Reset file selection if no files remain + if (viewer->file_count == 0) { + viewer->selected_file = 0; + viewer->file_line_count = 0; + viewer->file_scroll_offset = 0; + } else if (viewer->selected_file >= viewer->file_count) { + viewer->selected_file = viewer->file_count - 1; + } + + // Reload current file if any files exist + if (viewer->file_count > 0 && viewer->selected_file < viewer->file_count) { + load_full_file_with_diff(viewer, viewer->files[viewer->selected_file].filename); + } + } + } + break; + + case 'd': + case 'D': // Drop stash (removes without applying) + if (viewer->stash_count > 0 && viewer->selected_stash < viewer->stash_count) { + if (drop_git_stash(viewer->selected_stash)) { + // Refresh stash list after dropping + get_ncurses_git_stashes(viewer); + + // Adjust selected stash if needed + if (viewer->selected_stash >= viewer->stash_count && viewer->stash_count > 0) { + viewer->selected_stash = viewer->stash_count - 1; + } + } + } + break; + } } return 1; // Continue @@ -2216,15 +2314,15 @@ int run_ncurses_diff_viewer(void) { attron(COLOR_PAIR(3)); 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 " + "Git Diff Viewer: 1=files 2=view 3=commits 4=stashes | j/k=nav 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 " + "Git Diff Viewer: 1=files 2=view 3=commits 4=stashes | j/k=scroll " "Ctrl+U/D=30lines | q=quit"); } else { mvprintw(0, 0, - "Git Diff Viewer: 1=files 2=view 3=commits | j/k=nav P=push " + "Git Diff Viewer: 1=files 2=view 3=commits 4=stashes | j/k=nav P=push " "p=pull r/R=reset a=amend | q=quit"); } attroff(COLOR_PAIR(3)); @@ -2250,15 +2348,15 @@ int run_ncurses_diff_viewer(void) { attron(COLOR_PAIR(3)); if (viewer.current_mode == NCURSES_MODE_FILE_LIST) { mvprintw(0, 0, - "Git Diff Viewer: 1=files 2=view 3=commits | j/k=nav " + "Git Diff Viewer: 1=files 2=view 3=commits 4=stashes | j/k=nav " "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 " + "Git Diff Viewer: 1=files 2=view 3=commits 4=stashes | j/k=scroll " "Ctrl+U/D=30lines | q=quit"); } else { mvprintw(0, 0, - "Git Diff Viewer: 1=files 2=view 3=commits | j/k=nav P=push " + "Git Diff Viewer: 1=files 2=view 3=commits 4=stashes | j/k=nav P=push " "p=pull r/R=reset a=amend | q=quit"); } attroff(COLOR_PAIR(3)); @@ -2525,6 +2623,16 @@ void render_stash_list_window(NCursesDiffViewer *viewer) { for (int i = 0; i < max_stashes_visible && i < viewer->stash_count; i++) { int y = i + 1; + // Show selection indicator for stash list mode + if (i == viewer->selected_stash && + viewer->current_mode == NCURSES_MODE_STASH_LIST) { + wattron(viewer->stash_list_win, COLOR_PAIR(5)); + mvwprintw(viewer->stash_list_win, y, 1, ">"); + wattroff(viewer->stash_list_win, COLOR_PAIR(5)); + } else { + mvwprintw(viewer->stash_list_win, y, 1, " "); + } + // Show stash info (truncated to fit panel) int max_stash_len = viewer->file_panel_width - 4; char truncated_stash[256]; diff --git a/ncurses_diff_viewer.h b/ncurses_diff_viewer.h index 1b095e3..21609a7 100644 --- a/ncurses_diff_viewer.h +++ b/ncurses_diff_viewer.h @@ -43,7 +43,8 @@ typedef struct { typedef enum { NCURSES_MODE_FILE_LIST, NCURSES_MODE_FILE_VIEW, - NCURSES_MODE_COMMIT_LIST + NCURSES_MODE_COMMIT_LIST, + NCURSES_MODE_STASH_LIST } NCursesViewMode; typedef enum { @@ -80,6 +81,7 @@ typedef struct { int selected_commit; NCursesStash stashes[MAX_STASHES]; int stash_count; + int selected_stash; WINDOW *file_list_win; WINDOW *file_content_win; WINDOW *commit_list_win; diff --git a/ncurses_diff_viewer.o b/ncurses_diff_viewer.o index 5ac51a6..115a14c 100644 Binary files a/ncurses_diff_viewer.o and b/ncurses_diff_viewer.o differ diff --git a/shell b/shell index 5ed76c2..247373c 100755 Binary files a/shell and b/shell differ