gitbash 1.9.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +100 -89
- package/bin/gitbash +345 -723
- package/commands/_utils.sh +485 -157
- package/commands/branch.sh +9 -12
- package/commands/cleanstash.sh +47 -122
- package/commands/cleanup.sh +305 -441
- package/commands/commit.sh +136 -172
- package/commands/commits.sh +81 -114
- package/commands/create.sh +231 -314
- package/commands/pr.sh +158 -146
- package/commands/stale.sh +137 -277
- package/commands/stash.sh +11 -39
- package/commands/stashes.sh +10 -13
- package/commands/status.sh +197 -250
- package/commands/switch.sh +186 -256
- package/commands/unstash.sh +45 -109
- package/commands/update.sh +123 -142
- package/docs/CONTRIBUTING.md +16 -5
- package/docs/SECURITY.md +14 -3
- package/package.json +5 -3
package/commands/stashes.sh
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# shellcheck disable=SC2155
|
|
3
2
|
|
|
4
3
|
# Source common utilities
|
|
5
|
-
SOURCE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]
|
|
4
|
+
SOURCE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
6
5
|
# shellcheck source=_utils.sh
|
|
7
6
|
source "$SOURCE_DIR/_utils.sh"
|
|
8
7
|
|
|
@@ -62,7 +61,7 @@ Examples:
|
|
|
62
61
|
✖ Abort
|
|
63
62
|
|
|
64
63
|
Applying stash@{0} ...
|
|
65
|
-
Drop stash@{0} now? (
|
|
64
|
+
Drop stash@{0} now? (Y/n): y
|
|
66
65
|
|
|
67
66
|
---
|
|
68
67
|
|
|
@@ -91,8 +90,7 @@ Actions:
|
|
|
91
90
|
🗑️ Clean up - Delete stashes without applying (supports multi-select)
|
|
92
91
|
|
|
93
92
|
Requirements:
|
|
94
|
-
- fzf (fuzzy finder)
|
|
95
|
-
- stash, unstash and cleanstash functions must be available
|
|
93
|
+
- fzf (fuzzy finder)
|
|
96
94
|
|
|
97
95
|
See also:
|
|
98
96
|
stash -h Show help for stash command
|
|
@@ -130,20 +128,19 @@ EOF
|
|
|
130
128
|
# 3. Run fzf menu
|
|
131
129
|
# -----------------------------
|
|
132
130
|
local selection
|
|
133
|
-
selection=$(
|
|
131
|
+
selection=$(run_fzf --prompt="Stash action > " \
|
|
134
132
|
-i \
|
|
135
133
|
--reverse \
|
|
136
134
|
--border \
|
|
137
135
|
--header="What would you like to do?" \
|
|
138
136
|
--no-multi \
|
|
139
|
-
--bind=enter:accept \
|
|
140
137
|
<<< "$choices"
|
|
141
|
-
)
|
|
138
|
+
) || true
|
|
142
139
|
|
|
143
140
|
# ESC or Ctrl-C
|
|
144
141
|
if [[ -z "$selection" ]]; then
|
|
145
142
|
echo "Aborted."
|
|
146
|
-
return
|
|
143
|
+
return 0
|
|
147
144
|
fi
|
|
148
145
|
|
|
149
146
|
# -----------------------------
|
|
@@ -151,17 +148,17 @@ EOF
|
|
|
151
148
|
# -----------------------------
|
|
152
149
|
case "$selection" in
|
|
153
150
|
"$stash_option")
|
|
154
|
-
|
|
151
|
+
gb_run stash
|
|
155
152
|
;;
|
|
156
153
|
"$unstash_option")
|
|
157
|
-
|
|
154
|
+
gb_run unstash
|
|
158
155
|
;;
|
|
159
156
|
"$cleanup_option")
|
|
160
|
-
|
|
157
|
+
gb_run cleanstash
|
|
161
158
|
;;
|
|
162
159
|
"$abort_label")
|
|
163
160
|
echo "Aborted."
|
|
164
|
-
return
|
|
161
|
+
return 0
|
|
165
162
|
;;
|
|
166
163
|
*)
|
|
167
164
|
echo "Unknown selection."
|
package/commands/status.sh
CHANGED
|
@@ -1,16 +1,45 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# shellcheck disable=SC2155
|
|
3
2
|
|
|
4
3
|
# Source common utilities
|
|
5
|
-
SOURCE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]
|
|
4
|
+
SOURCE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
6
5
|
# shellcheck source=_utils.sh
|
|
7
6
|
source "$SOURCE_DIR/_utils.sh"
|
|
8
7
|
|
|
8
|
+
# Print one line per changed file: "<display>\t<XY>\t<path>\t<original path>"
|
|
9
|
+
# The path fields are exact (spaces, quotes and renames are handled).
|
|
10
|
+
_status_list() {
|
|
11
|
+
local entry xy path orig label display
|
|
12
|
+
while IFS= read -r -d '' entry; do
|
|
13
|
+
xy="${entry:0:2}"
|
|
14
|
+
path="${entry:3}"
|
|
15
|
+
orig=""
|
|
16
|
+
if [[ "${xy:0:1}" == [RC] || "${xy:1:1}" == [RC] ]]; then
|
|
17
|
+
IFS= read -r -d '' orig
|
|
18
|
+
fi
|
|
19
|
+
case "$xy" in
|
|
20
|
+
"??") label="UNTRACKED" ;;
|
|
21
|
+
DD|AU|UD|UA|DU|AA|UU) label="CONFLICT" ;;
|
|
22
|
+
" "?) label="UNSTAGED" ;;
|
|
23
|
+
?" ") label="STAGED" ;;
|
|
24
|
+
*) label="PARTIAL" ;;
|
|
25
|
+
esac
|
|
26
|
+
display="$path"
|
|
27
|
+
[[ -n "$orig" ]] && display="$orig -> $path"
|
|
28
|
+
printf '%-12s %s %s\t%s\t%s\t%s\n' "[$label]" "$xy" "$display" "$xy" "$path" "$orig"
|
|
29
|
+
done < <(git status --porcelain=v1 -z 2>/dev/null)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
# Unstage a file (works before the first commit, too)
|
|
33
|
+
_status_unstage() {
|
|
34
|
+
if git rev-parse --verify --quiet HEAD >/dev/null; then
|
|
35
|
+
git restore --staged -- "$@"
|
|
36
|
+
else
|
|
37
|
+
git rm --cached --quiet -r -- "$@"
|
|
38
|
+
fi
|
|
39
|
+
}
|
|
40
|
+
|
|
9
41
|
# Show git status with fzf file selector and diff preview
|
|
10
42
|
status() {
|
|
11
|
-
# -----------------------------
|
|
12
|
-
# 0. Check for help/version flag
|
|
13
|
-
# -----------------------------
|
|
14
43
|
if [[ "${1:-}" == "-v" || "${1:-}" == "--version" ]]; then
|
|
15
44
|
echo "gitbash ${FUNCNAME[0]} v$VERSION"
|
|
16
45
|
return 0
|
|
@@ -19,296 +48,214 @@ status() {
|
|
|
19
48
|
cat << 'EOF'
|
|
20
49
|
Usage: status [OPTIONS]
|
|
21
50
|
|
|
22
|
-
Interactive git status
|
|
23
|
-
Browse uncommitted changes and view diffs in preview pane.
|
|
51
|
+
Interactive git status: stage, unstage, discard and commit with diff previews.
|
|
24
52
|
|
|
25
53
|
Options:
|
|
26
54
|
-h, --help Show this help message
|
|
27
55
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
-
|
|
56
|
+
Labels:
|
|
57
|
+
[STAGED] All changes of the file are staged
|
|
58
|
+
[PARTIAL] Some changes staged, some not
|
|
59
|
+
[UNSTAGED] Changes not staged yet
|
|
60
|
+
[UNTRACKED] New file or directory
|
|
61
|
+
[CONFLICT] Merge conflict - resolve it in your editor
|
|
34
62
|
|
|
35
|
-
|
|
36
|
-
M Modified (unstaged)
|
|
37
|
-
A Added (staged)
|
|
38
|
-
D Deleted
|
|
39
|
-
R Renamed
|
|
40
|
-
?? Untracked
|
|
41
|
-
|
|
42
|
-
Navigation:
|
|
43
|
-
↑/↓ or j/k Navigate through files
|
|
63
|
+
Keys:
|
|
44
64
|
TAB Select/deselect file (multi-select)
|
|
45
|
-
Enter
|
|
46
|
-
Ctrl-R
|
|
65
|
+
Enter Stage the selected files (unstage them if fully staged)
|
|
66
|
+
Ctrl-R Discard changes of the selected files (asks first)
|
|
67
|
+
Ctrl-O Commit the staged changes (then asks whether to push)
|
|
47
68
|
ESC/Ctrl-C Exit
|
|
48
69
|
|
|
49
|
-
Examples:
|
|
50
|
-
$ status
|
|
51
|
-
Git Status >
|
|
52
|
-
> M src/app.js
|
|
53
|
-
A src/new-feature.js
|
|
54
|
-
?? temp.txt
|
|
55
|
-
M README.md
|
|
56
|
-
|
|
57
|
-
# Preview shows diff of selected file
|
|
58
|
-
# Navigate with arrow keys or j/k
|
|
59
|
-
|
|
60
70
|
Requirements:
|
|
61
71
|
- Must be in a git repository
|
|
62
|
-
- fzf
|
|
63
|
-
|
|
64
|
-
Notes:
|
|
65
|
-
- Staged changes show diff with --cached
|
|
66
|
-
- Unstaged changes show working tree diff
|
|
67
|
-
- Untracked files show file contents
|
|
68
|
-
- Empty status means working tree is clean
|
|
72
|
+
- fzf
|
|
69
73
|
|
|
70
74
|
EOF
|
|
71
75
|
return 0
|
|
72
76
|
fi
|
|
73
77
|
|
|
74
|
-
# -----------------------------
|
|
75
|
-
# 1. Check prerequisites
|
|
76
|
-
# -----------------------------
|
|
77
78
|
require_git_repo || return 1
|
|
78
79
|
require_fzf || return 1
|
|
79
80
|
|
|
80
|
-
#
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
status_output=$(git status --short | awk '{
|
|
85
|
-
index_status = substr($0, 1, 1);
|
|
86
|
-
work_status = substr($0, 2, 1);
|
|
87
|
-
file = substr($0, 4);
|
|
88
|
-
|
|
89
|
-
# Determine staging status
|
|
90
|
-
if (index_status ~ /[MADRC]/) {
|
|
91
|
-
staged = "[STAGED]";
|
|
92
|
-
} else if (work_status == "?" || index_status == "?") {
|
|
93
|
-
staged = "[UNTRACKED]";
|
|
94
|
-
} else {
|
|
95
|
-
staged = "[UNSTAGED]";
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
printf "%-12s %s %s\n", staged, substr($0, 1, 2), file;
|
|
99
|
-
}')
|
|
81
|
+
# Commands on file paths must run from the repository root
|
|
82
|
+
local repo_root
|
|
83
|
+
repo_root=$(git rev-parse --show-toplevel) || return 1
|
|
84
|
+
cd "$repo_root" || return 1
|
|
100
85
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
86
|
+
local pager bat_cmd preview
|
|
87
|
+
pager=$(gb_diff_pager)
|
|
88
|
+
bat_cmd=$(gb_bat_cmd)
|
|
89
|
+
preview='
|
|
90
|
+
xy=$(printf "%s" {} | cut -f2); file=$(printf "%s" {} | cut -f3)
|
|
91
|
+
if [[ "$xy" == "??" ]]; then
|
|
92
|
+
echo "=== UNTRACKED ==="; echo
|
|
93
|
+
if [[ -d "$file" ]]; then ls -la "$file"; else __BAT__ "$file" 2>/dev/null || echo "Cannot preview file"; fi
|
|
94
|
+
else
|
|
95
|
+
staged=$(git diff --cached --color=always -- "$file" 2>/dev/null)
|
|
96
|
+
unstaged=$(git diff --color=always -- "$file" 2>/dev/null)
|
|
97
|
+
if [[ -n "$staged" ]]; then echo "=== STAGED CHANGES ==="; echo; printf "%s\n" "$staged" | __PAGER__; fi
|
|
98
|
+
if [[ -n "$unstaged" ]]; then
|
|
99
|
+
[[ -n "$staged" ]] && echo
|
|
100
|
+
echo "=== UNSTAGED CHANGES ==="; echo; printf "%s\n" "$unstaged" | __PAGER__
|
|
101
|
+
fi
|
|
102
|
+
fi'
|
|
103
|
+
preview="${preview//__PAGER__/$pager}"
|
|
104
|
+
preview="${preview//__BAT__/$bat_cmd}"
|
|
105
105
|
|
|
106
|
-
|
|
107
|
-
# 3. Run fzf with diff preview in a loop
|
|
108
|
-
# -----------------------------
|
|
106
|
+
local status_list
|
|
109
107
|
while true; do
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
108
|
+
status_list=$(_status_list)
|
|
109
|
+
if [[ -z "$status_list" ]]; then
|
|
110
|
+
print_success "Working tree clean."
|
|
111
|
+
return 0
|
|
112
|
+
fi
|
|
113
|
+
|
|
114
|
+
local output
|
|
115
|
+
output=$(run_fzf \
|
|
113
116
|
--height=100% \
|
|
114
117
|
-i \
|
|
115
118
|
--reverse \
|
|
116
119
|
--border \
|
|
117
120
|
--prompt="Git Status > " \
|
|
118
|
-
--header="[Enter]
|
|
121
|
+
--header="[Enter] stage/unstage | [Ctrl-R] discard | [Ctrl-O] commit staged | [TAB] multi-select | [ESC] exit" \
|
|
119
122
|
--multi \
|
|
120
|
-
--
|
|
121
|
-
--
|
|
122
|
-
--
|
|
123
|
-
|
|
124
|
-
file_status=$(echo {} | awk "{print \$2}");
|
|
125
|
-
|
|
126
|
-
# Show diff based on file status
|
|
127
|
-
if [[ "$file_status" == "??" ]]; then
|
|
128
|
-
# Untracked file - show contents
|
|
129
|
-
echo "=== UNTRACKED FILE ===";
|
|
130
|
-
echo;
|
|
131
|
-
bat --color=always --style=numbers --theme=\"GitHub\" "$file" 2>/dev/null || cat "$file" 2>/dev/null || echo "Cannot preview file";
|
|
132
|
-
elif [[ "$file_status" =~ ^[MAC] ]]; then
|
|
133
|
-
# Staged file - show cached diff
|
|
134
|
-
staged_diff=$(git diff --cached --color=always "$file" 2>/dev/null);
|
|
135
|
-
unstaged_diff=$(git diff --color=always "$file" 2>/dev/null);
|
|
136
|
-
|
|
137
|
-
# Only show staged section if there are staged changes
|
|
138
|
-
if [[ -n "$staged_diff" ]]; then
|
|
139
|
-
echo "=== STAGED CHANGES ===";
|
|
140
|
-
echo;
|
|
141
|
-
echo "$staged_diff" | delta --light 2>/dev/null || echo "$staged_diff";
|
|
142
|
-
fi
|
|
143
|
-
|
|
144
|
-
# Only show unstaged section if there are unstaged changes
|
|
145
|
-
if [[ -n "$unstaged_diff" ]]; then
|
|
146
|
-
[[ -n "$staged_diff" ]] && echo;
|
|
147
|
-
echo "=== UNSTAGED CHANGES ===";
|
|
148
|
-
echo;
|
|
149
|
-
echo "$unstaged_diff" | delta --light 2>/dev/null || echo "$unstaged_diff";
|
|
150
|
-
fi
|
|
151
|
-
else
|
|
152
|
-
# Unstaged changes
|
|
153
|
-
unstaged_diff=$(git diff --color=always "$file" 2>/dev/null);
|
|
154
|
-
if [[ -n "$unstaged_diff" ]]; then
|
|
155
|
-
echo "=== UNSTAGED CHANGES ===";
|
|
156
|
-
echo;
|
|
157
|
-
echo "$unstaged_diff" | delta --light 2>/dev/null || echo "$unstaged_diff";
|
|
158
|
-
fi
|
|
159
|
-
fi
|
|
160
|
-
' \
|
|
123
|
+
--delimiter=$'\t' \
|
|
124
|
+
--with-nth=1 \
|
|
125
|
+
--expect=ctrl-r,ctrl-o \
|
|
126
|
+
--preview="$preview" \
|
|
161
127
|
--preview-window=right:60% \
|
|
162
|
-
<<< "$
|
|
163
|
-
)
|
|
128
|
+
<<< "$status_list"
|
|
129
|
+
) || true
|
|
164
130
|
|
|
165
|
-
# Parse the key pressed and the selected files
|
|
166
131
|
local key_pressed
|
|
167
|
-
key_pressed=$(
|
|
168
|
-
|
|
169
|
-
# Get all selected files (skip first line which is the key)
|
|
170
|
-
local selected_files=()
|
|
132
|
+
key_pressed=$(printf '%s\n' "$output" | head -n 1)
|
|
133
|
+
local selected=() line
|
|
171
134
|
while IFS= read -r line; do
|
|
172
|
-
[[ -n "$line" ]] &&
|
|
173
|
-
done
|
|
135
|
+
[[ -n "$line" ]] && selected+=("$line")
|
|
136
|
+
done < <(printf '%s\n' "$output" | tail -n +2)
|
|
174
137
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
echo "Exited status viewer."
|
|
178
|
-
break
|
|
138
|
+
if [[ -z "$output" ]]; then
|
|
139
|
+
return 0
|
|
179
140
|
fi
|
|
180
141
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
142
|
+
case "$key_pressed" in
|
|
143
|
+
ctrl-o)
|
|
144
|
+
if git diff --cached --quiet 2>/dev/null; then
|
|
145
|
+
print_warning "Nothing staged yet. Stage files with Enter first."
|
|
146
|
+
continue
|
|
147
|
+
fi
|
|
148
|
+
if gb_run commit --staged; then
|
|
149
|
+
if gb_confirm "Push to the remote?" n; then
|
|
150
|
+
gb_sync_and_push
|
|
151
|
+
fi
|
|
152
|
+
fi
|
|
153
|
+
continue
|
|
154
|
+
;;
|
|
155
|
+
ctrl-r)
|
|
156
|
+
[[ ${#selected[@]} -eq 0 ]] && continue
|
|
157
|
+
_status_discard "${selected[@]}"
|
|
158
|
+
continue
|
|
159
|
+
;;
|
|
160
|
+
esac
|
|
161
|
+
|
|
162
|
+
[[ ${#selected[@]} -eq 0 ]] && return 0
|
|
163
|
+
|
|
164
|
+
# Enter: stage, or unstage fully staged files
|
|
165
|
+
local xy path orig
|
|
166
|
+
for line in "${selected[@]}"; do
|
|
167
|
+
xy=$(printf '%s' "$line" | cut -f2)
|
|
168
|
+
path=$(printf '%s' "$line" | cut -f3)
|
|
169
|
+
orig=$(printf '%s' "$line" | cut -f4)
|
|
170
|
+
case "$xy" in
|
|
171
|
+
DD|AU|UD|UA|DU|AA|UU)
|
|
172
|
+
print_warning "$path has a merge conflict - resolve it in your editor, then stage it."
|
|
173
|
+
;;
|
|
174
|
+
?" ")
|
|
175
|
+
echo "Unstaging: $path"
|
|
176
|
+
if [[ -n "$orig" ]]; then
|
|
177
|
+
_status_unstage "$path" "$orig" || print_error "Failed to unstage $path"
|
|
178
|
+
else
|
|
179
|
+
_status_unstage "$path" || print_error "Failed to unstage $path"
|
|
180
|
+
fi
|
|
181
|
+
;;
|
|
182
|
+
*)
|
|
183
|
+
echo "Staging: $path"
|
|
184
|
+
git add -A -- "$path" || print_error "Failed to stage $path"
|
|
185
|
+
;;
|
|
186
|
+
esac
|
|
187
|
+
done
|
|
188
|
+
done
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
# Discard changes of the selected status lines (asks first)
|
|
192
|
+
_status_discard() {
|
|
193
|
+
local line xy path orig
|
|
194
|
+
local untracked=() added=() tracked=() tracked_orig=()
|
|
195
|
+
for line in "$@"; do
|
|
196
|
+
xy=$(printf '%s' "$line" | cut -f2)
|
|
197
|
+
path=$(printf '%s' "$line" | cut -f3)
|
|
198
|
+
orig=$(printf '%s' "$line" | cut -f4)
|
|
199
|
+
case "$xy" in
|
|
200
|
+
"??") untracked+=("$path") ;;
|
|
201
|
+
A?) added+=("$path") ;;
|
|
202
|
+
*) tracked+=("$path"); [[ -n "$orig" ]] && tracked_orig+=("$orig") ;;
|
|
203
|
+
esac
|
|
204
|
+
done
|
|
205
|
+
|
|
206
|
+
if [[ ${#untracked[@]} -gt 0 ]]; then
|
|
207
|
+
echo "Untracked files/directories to delete:"
|
|
208
|
+
printf ' - %s\n' "${untracked[@]}"
|
|
209
|
+
if gb_confirm --strict "Delete these ${#untracked[@]} item(s)? This cannot be undone." n; then
|
|
210
|
+
for path in "${untracked[@]}"; do
|
|
211
|
+
if rm -rf -- "$path"; then
|
|
212
|
+
print_success "Deleted: $path"
|
|
195
213
|
else
|
|
196
|
-
|
|
214
|
+
print_error "Failed to delete: $path"
|
|
197
215
|
fi
|
|
198
216
|
done
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
if [[ ${#untracked_to_delete[@]} -gt 0 ]]; then
|
|
202
|
-
echo "Untracked files to delete:"
|
|
203
|
-
for f in "${untracked_to_delete[@]}"; do
|
|
204
|
-
echo " - $f"
|
|
205
|
-
done
|
|
206
|
-
prompt_read "Delete these ${#untracked_to_delete[@]} untracked file(s)? (y/N): " confirm
|
|
207
|
-
case "$confirm" in
|
|
208
|
-
[yY][eE][sS]|[yY])
|
|
209
|
-
for f in "${untracked_to_delete[@]}"; do
|
|
210
|
-
rm "$f"
|
|
211
|
-
echo "✓ Deleted: $f"
|
|
212
|
-
done
|
|
213
|
-
;;
|
|
214
|
-
*)
|
|
215
|
-
echo "Skipped deletion."
|
|
216
|
-
;;
|
|
217
|
-
esac
|
|
218
|
-
fi
|
|
219
|
-
|
|
220
|
-
# Handle tracked files
|
|
221
|
-
if [[ ${#files_to_revert[@]} -gt 0 ]]; then
|
|
222
|
-
echo "Files to revert:"
|
|
223
|
-
for f in "${files_to_revert[@]}"; do
|
|
224
|
-
echo " - $f"
|
|
225
|
-
done
|
|
226
|
-
prompt_read "Revert changes in these ${#files_to_revert[@]} file(s)? This cannot be undone. (y/N): " confirm
|
|
227
|
-
case "$confirm" in
|
|
228
|
-
[yY][eE][sS]|[yY])
|
|
229
|
-
for f in "${files_to_revert[@]}"; do
|
|
230
|
-
git reset HEAD "$f" 2>/dev/null
|
|
231
|
-
git checkout -- "$f"
|
|
232
|
-
echo "✓ Reverted: $f"
|
|
233
|
-
done
|
|
234
|
-
;;
|
|
235
|
-
*)
|
|
236
|
-
echo "Skipped revert."
|
|
237
|
-
;;
|
|
238
|
-
esac
|
|
239
|
-
fi
|
|
240
|
-
else
|
|
241
|
-
# Toggle staging for all selected files
|
|
242
|
-
local did_stage=false
|
|
243
|
-
for selected_file in "${selected_files[@]}"; do
|
|
244
|
-
local staging_status=$(echo "$selected_file" | awk '{print $1}')
|
|
245
|
-
local filename=$(echo "$selected_file" | awk '{print $NF}')
|
|
217
|
+
fi
|
|
218
|
+
fi
|
|
246
219
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
220
|
+
if [[ ${#added[@]} -gt 0 ]]; then
|
|
221
|
+
echo "Newly added files to unstage:"
|
|
222
|
+
printf ' - %s\n' "${added[@]}"
|
|
223
|
+
if gb_confirm --strict "Unstage these ${#added[@]} file(s)?" n; then
|
|
224
|
+
local delete_too=false
|
|
225
|
+
gb_confirm --strict "Also delete them from disk?" n && delete_too=true
|
|
226
|
+
for path in "${added[@]}"; do
|
|
227
|
+
if ! git rm --cached --quiet -r -- "$path"; then
|
|
228
|
+
print_error "Failed to unstage: $path"
|
|
229
|
+
continue
|
|
230
|
+
fi
|
|
231
|
+
if [[ "$delete_too" == true ]]; then
|
|
232
|
+
rm -rf -- "$path" && print_success "Deleted: $path"
|
|
233
|
+
else
|
|
234
|
+
print_success "Unstaged (file kept): $path"
|
|
258
235
|
fi
|
|
259
236
|
done
|
|
260
237
|
fi
|
|
238
|
+
fi
|
|
261
239
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
file = substr($0, 4);
|
|
267
|
-
|
|
268
|
-
# Determine staging status
|
|
269
|
-
if (index_status ~ /[MADRC]/) {
|
|
270
|
-
staged = "[STAGED]";
|
|
271
|
-
} else if (work_status == "?" || index_status == "?") {
|
|
272
|
-
staged = "[UNTRACKED]";
|
|
273
|
-
} else {
|
|
274
|
-
staged = "[UNSTAGED]";
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
printf "%-12s %s %s\n", staged, substr($0, 1, 2), file;
|
|
278
|
-
}')
|
|
279
|
-
|
|
280
|
-
# Check if working tree is now clean
|
|
281
|
-
if [[ -z "$status_output" ]]; then
|
|
282
|
-
echo "✓ Working tree is now clean - all changes staged or reverted."
|
|
283
|
-
break
|
|
240
|
+
if [[ ${#tracked[@]} -gt 0 ]]; then
|
|
241
|
+
if ! git rev-parse --verify --quiet HEAD >/dev/null; then
|
|
242
|
+
print_warning "No commits yet - nothing to restore tracked files from."
|
|
243
|
+
return 0
|
|
284
244
|
fi
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
if
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
git diff --cached --name-only | while IFS= read -r file; do
|
|
293
|
-
# Get the directory and filename
|
|
294
|
-
dir=$(dirname "$file")
|
|
295
|
-
base=$(basename "$file")
|
|
296
|
-
|
|
297
|
-
# Simple tree-like output with indentation
|
|
298
|
-
if [[ "$dir" == "." ]]; then
|
|
299
|
-
echo " └─ $base"
|
|
245
|
+
echo "Files to restore to the last commit:"
|
|
246
|
+
printf ' - %s\n' "${tracked[@]}"
|
|
247
|
+
if gb_confirm --strict "Discard all changes in these ${#tracked[@]} file(s)? This cannot be undone." n; then
|
|
248
|
+
for path in "${tracked[@]}"; do
|
|
249
|
+
if git restore --source=HEAD --staged --worktree -- "$path" 2>/dev/null ||
|
|
250
|
+
git restore --staged -- "$path" 2>/dev/null; then
|
|
251
|
+
print_success "Restored: $path"
|
|
300
252
|
else
|
|
301
|
-
|
|
253
|
+
print_error "Failed to restore: $path"
|
|
302
254
|
fi
|
|
303
255
|
done
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
echo "Press Ctrl+C to abort."
|
|
308
|
-
echo
|
|
309
|
-
# Directly prompt for commit message (user can Ctrl+C to abort)
|
|
310
|
-
gitbash commit -s -p
|
|
311
|
-
break
|
|
256
|
+
if [[ ${#tracked_orig[@]} -gt 0 ]]; then
|
|
257
|
+
git restore --source=HEAD --staged --worktree -- "${tracked_orig[@]}" 2>/dev/null
|
|
258
|
+
fi
|
|
312
259
|
fi
|
|
313
|
-
|
|
260
|
+
fi
|
|
314
261
|
}
|