gitbash 1.9.1 → 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 -274
- 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/unstash.sh
CHANGED
|
@@ -1,15 +1,25 @@
|
|
|
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
|
+
# fzf preview command for a stash list line
|
|
9
|
+
_stash_preview() {
|
|
10
|
+
local pager
|
|
11
|
+
pager=$(gb_diff_pager)
|
|
12
|
+
printf '%s' '
|
|
13
|
+
sel=$(printf "%s" {} | grep -o "^stash@{[0-9]*}" || true)
|
|
14
|
+
if [[ -n "$sel" ]]; then
|
|
15
|
+
{ git stash show --include-untracked -p --color=always "$sel" 2>/dev/null ||
|
|
16
|
+
git stash show -p --color=always "$sel" 2>/dev/null; } | '"$pager"'
|
|
17
|
+
else
|
|
18
|
+
echo "No preview"
|
|
19
|
+
fi'
|
|
20
|
+
}
|
|
21
|
+
|
|
9
22
|
unstash() {
|
|
10
|
-
# -----------------------------
|
|
11
|
-
# 0. Check for help/version flag
|
|
12
|
-
# -----------------------------
|
|
13
23
|
if [[ "${1:-}" == "-v" || "${1:-}" == "--version" ]]; then
|
|
14
24
|
echo "gitbash ${FUNCNAME[0]} v$VERSION"
|
|
15
25
|
return 0
|
|
@@ -24,139 +34,65 @@ Interactive fuzzy finder (fzf) with preview support.
|
|
|
24
34
|
Options:
|
|
25
35
|
-h, --help Show this help message
|
|
26
36
|
|
|
27
|
-
Features:
|
|
28
|
-
- Single-select stash picker
|
|
29
|
-
- Preview stash contents before applying
|
|
30
|
-
- Apply stash to working directory
|
|
31
|
-
- Optional confirmation to drop stash after applying
|
|
32
|
-
|
|
33
|
-
Navigation:
|
|
34
|
-
↑/↓ or j/k Navigate through stashes
|
|
35
|
-
Enter Select and apply stash
|
|
36
|
-
ESC/Ctrl-C Abort
|
|
37
|
-
|
|
38
|
-
Examples:
|
|
39
|
-
$ unstash
|
|
40
|
-
Available stashes >
|
|
41
|
-
> stash@{0}: WIP on main: 1a2b3c4 commit message
|
|
42
|
-
stash@{1}: WIP on feature: 5d6e7f8 another commit
|
|
43
|
-
stash@{2}: On dev: 9a8b7c6 old work
|
|
44
|
-
✖ Abort
|
|
45
|
-
|
|
46
|
-
# Navigate to desired stash and press Enter
|
|
47
|
-
|
|
48
|
-
Applying stash@{1} ...
|
|
49
|
-
On branch main
|
|
50
|
-
Changes not staged for commit:
|
|
51
|
-
modified: src/app.js
|
|
52
|
-
|
|
53
|
-
Drop stash@{1} now? (y/N): y
|
|
54
|
-
Dropping stash@{1} ...
|
|
55
|
-
Dropped stash@{1} (5d6e7f8abcd...)
|
|
56
|
-
|
|
57
37
|
Workflow:
|
|
58
|
-
1. Select a stash from the list
|
|
59
|
-
2.
|
|
60
|
-
3. Choose whether to drop (delete)
|
|
61
|
-
|
|
62
|
-
Requirements:
|
|
63
|
-
- fzf (fuzzy finder) - will prompt to install if not found
|
|
64
|
-
- git stash list with at least one stash
|
|
38
|
+
1. Select a stash from the list (preview shows its changes)
|
|
39
|
+
2. The stash is applied with 'git stash apply'
|
|
40
|
+
3. Choose whether to drop (delete) it (default: yes)
|
|
65
41
|
|
|
66
42
|
Notes:
|
|
67
|
-
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
-
|
|
43
|
+
- If applying fails or causes conflicts, the stash is kept
|
|
44
|
+
|
|
45
|
+
Requirements:
|
|
46
|
+
- fzf (fuzzy finder)
|
|
71
47
|
|
|
72
48
|
EOF
|
|
73
49
|
return 0
|
|
74
50
|
fi
|
|
75
51
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
52
|
+
require_git_repo || return 1
|
|
53
|
+
|
|
54
|
+
local stash_list
|
|
55
|
+
stash_list=$(git stash list)
|
|
56
|
+
if [[ -z "$stash_list" ]]; then
|
|
57
|
+
echo "No stashes."
|
|
58
|
+
return 0
|
|
59
|
+
fi
|
|
60
|
+
|
|
79
61
|
require_fzf || return 1
|
|
80
62
|
|
|
81
|
-
# -----------------------------
|
|
82
|
-
# 2. Build menu entries
|
|
83
|
-
# -----------------------------
|
|
84
|
-
local abort_label="✖ Abort"
|
|
85
|
-
|
|
86
|
-
local choices
|
|
87
|
-
choices=$(
|
|
88
|
-
{
|
|
89
|
-
git stash list
|
|
90
|
-
echo "$abort_label"
|
|
91
|
-
}
|
|
92
|
-
)
|
|
93
|
-
|
|
94
|
-
# -----------------------------
|
|
95
|
-
# 3. Run fzf full-screen picker with preview
|
|
96
|
-
# -----------------------------
|
|
97
63
|
local selection
|
|
98
|
-
selection=$(
|
|
64
|
+
selection=$(run_fzf --prompt="Available stashes > " \
|
|
99
65
|
-i \
|
|
100
66
|
--reverse \
|
|
101
67
|
--border \
|
|
102
68
|
--header="Select a stash to apply" \
|
|
103
69
|
--no-multi \
|
|
104
|
-
--
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
if [[ -n "$sel" ]]; then
|
|
108
|
-
git stash show -p "$sel" | delta --light 2>/dev/null || git stash show -p "$sel";
|
|
109
|
-
else
|
|
110
|
-
echo "No preview";
|
|
111
|
-
fi
|
|
112
|
-
' \
|
|
113
|
-
<<< "$choices"
|
|
114
|
-
) </dev/tty || true
|
|
115
|
-
|
|
116
|
-
# ESC or Ctrl-C
|
|
117
|
-
if [[ -z "$selection" ]]; then
|
|
118
|
-
echo "Aborted."
|
|
119
|
-
return 1
|
|
120
|
-
fi
|
|
70
|
+
--preview="$(_stash_preview)" \
|
|
71
|
+
<<< "$stash_list"
|
|
72
|
+
) || true
|
|
121
73
|
|
|
122
|
-
|
|
123
|
-
if [[ "$selection" == "$abort_label" ]]; then
|
|
74
|
+
if [[ -z "$selection" ]]; then
|
|
124
75
|
echo "Aborted."
|
|
125
|
-
return
|
|
76
|
+
return 0
|
|
126
77
|
fi
|
|
127
78
|
|
|
128
|
-
# -----------------------------
|
|
129
|
-
# 4. Extract stash ID safely
|
|
130
|
-
# -----------------------------
|
|
131
79
|
local stash_id
|
|
132
|
-
stash_id=$(
|
|
133
|
-
|
|
80
|
+
stash_id=$(printf '%s' "$selection" | grep -o '^stash@{[0-9]*}' || true)
|
|
134
81
|
if [[ -z "$stash_id" ]]; then
|
|
135
|
-
|
|
82
|
+
print_error "Could not determine the stash ID."
|
|
136
83
|
return 1
|
|
137
84
|
fi
|
|
138
85
|
|
|
139
|
-
# -----------------------------
|
|
140
|
-
# 5. Apply the stash
|
|
141
|
-
# -----------------------------
|
|
142
86
|
echo "Applying $stash_id ..."
|
|
143
87
|
if ! git stash apply "$stash_id"; then
|
|
144
|
-
|
|
88
|
+
print_error "Apply failed or had conflicts - the stash was kept."
|
|
145
89
|
return 1
|
|
146
90
|
fi
|
|
147
91
|
|
|
148
|
-
# -----------------------------
|
|
149
|
-
# 6. Confirm before dropping (default: Yes)
|
|
150
|
-
# -----------------------------
|
|
151
92
|
echo
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
*)
|
|
158
|
-
echo "Dropping $stash_id ..."
|
|
159
|
-
git stash drop "$stash_id"
|
|
160
|
-
;;
|
|
161
|
-
esac
|
|
93
|
+
if gb_confirm "Drop $stash_id now?" y; then
|
|
94
|
+
git stash drop "$stash_id"
|
|
95
|
+
else
|
|
96
|
+
echo "Stash kept."
|
|
97
|
+
fi
|
|
162
98
|
}
|
package/commands/update.sh
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
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
|
|
|
9
|
-
#
|
|
8
|
+
# Returns 0 if the branch is checked out in any worktree
|
|
9
|
+
_update_is_checked_out() {
|
|
10
|
+
git worktree list --porcelain 2>/dev/null | grep -qxF "branch refs/heads/$1"
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
# Update the current branch with the latest version of the base branch
|
|
10
14
|
update() {
|
|
11
|
-
local merge_tool="${GITBASH_MERGE_COMMAND:-fork}"
|
|
12
15
|
local should_push=false
|
|
13
16
|
|
|
14
|
-
# Parse arguments
|
|
15
17
|
while [[ $# -gt 0 ]]; do
|
|
16
|
-
case $1 in
|
|
18
|
+
case "$1" in
|
|
17
19
|
-v|--version)
|
|
18
20
|
echo "gitbash ${FUNCNAME[0]} v$VERSION"
|
|
19
21
|
return 0
|
|
@@ -22,60 +24,30 @@ update() {
|
|
|
22
24
|
cat << 'EOF'
|
|
23
25
|
Usage: update [OPTIONS]
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
Merge the latest base branch (main/master) from the remote into the current branch.
|
|
26
28
|
|
|
27
29
|
Options:
|
|
28
|
-
-p, --push Push
|
|
30
|
+
-p, --push Push the branch after a successful merge
|
|
31
|
+
-y, --yes Don't ask for confirmations
|
|
29
32
|
-h, --help Show this help message
|
|
30
33
|
|
|
31
|
-
Configuration:
|
|
32
|
-
- Merge tool can be set by editing the 'GITBASH_MERGE_COMMAND' variable
|
|
33
|
-
- Default: "fork"
|
|
34
|
-
|
|
35
34
|
Behavior:
|
|
36
|
-
1. Detects
|
|
37
|
-
2.
|
|
38
|
-
3.
|
|
39
|
-
4.
|
|
40
|
-
5.
|
|
35
|
+
1. Detects the base branch (GITBASH_BASE_BRANCH, the remote's HEAD, main or master)
|
|
36
|
+
2. With local changes, asks: [c]ommit them, [s]tash them (restored afterwards) or [a]bort
|
|
37
|
+
3. Fetches the base branch and merges '<remote>/<base>' into your branch
|
|
38
|
+
4. Also fast-forwards your local base branch when it isn't checked out anywhere
|
|
39
|
+
5. On conflicts, lists the files and opens your merge tool (GITBASH_MERGE_COMMAND)
|
|
40
|
+
6. With --push, pushes the result (asks before rebasing if the remote diverged)
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
$ git checkout feature/LOVE-123-new-feature
|
|
44
|
-
$ update
|
|
45
|
-
Fetching latest 'main' from origin...
|
|
46
|
-
Merging updated 'main' into 'feature/LOVE-123-new-feature'...
|
|
47
|
-
✓ 'feature/LOVE-123-new-feature' is now up-to-date with 'main'.
|
|
42
|
+
On the base branch itself, fast-forwards it from the remote instead.
|
|
48
43
|
|
|
49
|
-
|
|
44
|
+
Configuration (run 'gitbash --config'):
|
|
45
|
+
GITBASH_MERGE_COMMAND Merge tool, e.g. "fork", "code", "gitkraken" (default: fork).
|
|
46
|
+
Only read from ~/.gitbashrc and .gitbashrc-user.
|
|
50
47
|
|
|
48
|
+
Examples:
|
|
49
|
+
$ update
|
|
51
50
|
$ update --push
|
|
52
|
-
Fetching latest 'main' from origin...
|
|
53
|
-
Merging updated 'main' into 'feature/LOVE-123-new-feature'...
|
|
54
|
-
✓ 'feature/LOVE-123-new-feature' is now up-to-date with 'main'.
|
|
55
|
-
Pushing 'feature/LOVE-123-new-feature' to origin...
|
|
56
|
-
✓ Successfully pushed 'feature/LOVE-123-new-feature' to origin.
|
|
57
|
-
|
|
58
|
-
---
|
|
59
|
-
|
|
60
|
-
$ update -p
|
|
61
|
-
Fetching latest 'main' from origin...
|
|
62
|
-
Merging updated 'main' into 'feature/dev-branch'...
|
|
63
|
-
⚠ Merge completed with conflicts. Resolve them manually.
|
|
64
|
-
# Opens configured merge tool for conflict resolution
|
|
65
|
-
|
|
66
|
-
Configuration:
|
|
67
|
-
- Merge tool can be changed by editing the 'GITBASH_MERGE_COMMAND' variable
|
|
68
|
-
- Options: "fork", "code", "gitkraken", "gitk", etc.
|
|
69
|
-
|
|
70
|
-
Requirements:
|
|
71
|
-
- Must be in a git repository
|
|
72
|
-
- Must be on a feature branch (not main/master)
|
|
73
|
-
- Remote 'origin' must exist
|
|
74
|
-
|
|
75
|
-
Notes:
|
|
76
|
-
- Uses fast-forward merge when possible
|
|
77
|
-
- Automatically opens merge tool if conflicts occur
|
|
78
|
-
- Local main/master is updated without checkout
|
|
79
51
|
|
|
80
52
|
EOF
|
|
81
53
|
return 0
|
|
@@ -84,120 +56,129 @@ EOF
|
|
|
84
56
|
should_push=true
|
|
85
57
|
shift
|
|
86
58
|
;;
|
|
59
|
+
-y|--yes)
|
|
60
|
+
GITBASH_ASSUME_YES=1
|
|
61
|
+
shift
|
|
62
|
+
;;
|
|
87
63
|
*)
|
|
88
|
-
|
|
89
|
-
echo "Usage: update [-p|--push]"
|
|
64
|
+
print_error "Unknown option: $1"
|
|
65
|
+
echo "Usage: update [-p|--push] [-y|--yes]" >&2
|
|
90
66
|
return 1
|
|
91
67
|
;;
|
|
92
68
|
esac
|
|
93
69
|
done
|
|
94
70
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
71
|
+
require_git_repo || return 1
|
|
72
|
+
|
|
73
|
+
local remote current_branch base_branch
|
|
74
|
+
remote=$(gb_remote)
|
|
75
|
+
if ! current_branch=$(gb_current_branch); then
|
|
76
|
+
print_error "Detached HEAD: check out a branch first."
|
|
98
77
|
return 1
|
|
99
78
|
fi
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
# Detect base branch: main or master
|
|
104
|
-
if git show-ref --verify --quiet refs/heads/main; then
|
|
105
|
-
base_branch="main"
|
|
106
|
-
elif git show-ref --verify --quiet refs/heads/master; then
|
|
107
|
-
base_branch="master"
|
|
108
|
-
else
|
|
109
|
-
echo "Could not detect 'main' or 'master' locally."
|
|
79
|
+
if ! base_branch=$(gb_base_branch); then
|
|
80
|
+
print_error "Could not detect the base branch. Set GITBASH_BASE_BRANCH with 'gitbash --config'."
|
|
110
81
|
return 1
|
|
111
82
|
fi
|
|
112
83
|
|
|
84
|
+
# -----------------------------
|
|
85
|
+
# On the base branch: just fast-forward it
|
|
86
|
+
# -----------------------------
|
|
113
87
|
if [[ "$current_branch" == "$base_branch" ]]; then
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
if [[ $? -eq 0 ]]; then
|
|
119
|
-
echo "✓ '$base_branch' is now up-to-date."
|
|
120
|
-
else
|
|
121
|
-
echo "⚠ Failed to pull latest changes."
|
|
122
|
-
return 1
|
|
88
|
+
print_info "Pulling latest '$base_branch' from '$remote'..."
|
|
89
|
+
if git pull --ff-only "$remote" "$base_branch"; then
|
|
90
|
+
print_success "'$base_branch' is up to date."
|
|
91
|
+
return 0
|
|
123
92
|
fi
|
|
124
|
-
|
|
93
|
+
print_error "Could not fast-forward '$base_branch'. It has local commits or local changes that block the update."
|
|
94
|
+
return 1
|
|
125
95
|
fi
|
|
126
96
|
|
|
127
|
-
#
|
|
97
|
+
# -----------------------------
|
|
98
|
+
# Local changes: commit, stash or abort
|
|
99
|
+
# -----------------------------
|
|
100
|
+
local stashed=false
|
|
128
101
|
if [[ -n "$(git status --porcelain)" ]]; then
|
|
129
|
-
echo ""
|
|
130
102
|
echo "You have uncommitted changes:"
|
|
131
103
|
git status --short
|
|
132
|
-
echo
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
104
|
+
echo
|
|
105
|
+
local choice="c"
|
|
106
|
+
gb_choice choice "[c]ommit them, [s]tash them for the update, or [a]bort? (C/s/a):" "csa" "c"
|
|
107
|
+
case "$choice" in
|
|
108
|
+
c)
|
|
109
|
+
if ! gb_run commit; then
|
|
110
|
+
print_error "Commit failed or was cancelled. Update aborted."
|
|
111
|
+
return 1
|
|
112
|
+
fi
|
|
113
|
+
;;
|
|
114
|
+
s)
|
|
115
|
+
if ! git stash push --include-untracked --quiet -m "gitbash update: auto-stash"; then
|
|
116
|
+
print_error "Could not stash the changes. Update aborted."
|
|
117
|
+
return 1
|
|
118
|
+
fi
|
|
119
|
+
stashed=true
|
|
120
|
+
print_info "Changes stashed. They are restored after the update."
|
|
121
|
+
;;
|
|
122
|
+
*)
|
|
123
|
+
echo "Update aborted."
|
|
124
|
+
return 1
|
|
125
|
+
;;
|
|
126
|
+
esac
|
|
143
127
|
fi
|
|
144
128
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
129
|
+
# -----------------------------
|
|
130
|
+
# Fetch and merge
|
|
131
|
+
# -----------------------------
|
|
132
|
+
print_info "Fetching latest '$base_branch' from '$remote'..."
|
|
133
|
+
if ! git fetch --quiet "$remote" "+refs/heads/$base_branch:refs/remotes/$remote/$base_branch"; then
|
|
134
|
+
print_error "Could not fetch '$base_branch' from '$remote'."
|
|
135
|
+
[[ "$stashed" == true ]] && git stash pop --quiet
|
|
148
136
|
return 1
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
git
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
echo "Remote has updates. Pulling first..."
|
|
168
|
-
git pull --rebase origin "$current_branch"
|
|
169
|
-
if [[ $? -ne 0 ]]; then
|
|
170
|
-
echo "⚠ Failed to pull remote changes. Please resolve conflicts and try again."
|
|
171
|
-
return 1
|
|
172
|
-
fi
|
|
173
|
-
echo "✓ Pulled latest changes."
|
|
174
|
-
elif ! git merge-base --is-ancestor "$remote_commit" "$local_commit" 2>/dev/null; then
|
|
175
|
-
# Branches have diverged
|
|
176
|
-
echo "Remote has diverged. Pulling with rebase..."
|
|
177
|
-
git pull --rebase origin "$current_branch"
|
|
178
|
-
if [[ $? -ne 0 ]]; then
|
|
179
|
-
echo "⚠ Failed to pull remote changes. Please resolve conflicts and try again."
|
|
180
|
-
return 1
|
|
181
|
-
fi
|
|
182
|
-
echo "✓ Rebased on latest changes."
|
|
183
|
-
fi
|
|
184
|
-
fi
|
|
185
|
-
|
|
186
|
-
echo "Pushing '$current_branch' to origin..."
|
|
187
|
-
git push origin "$current_branch"
|
|
188
|
-
if [[ $? -eq 0 ]]; then
|
|
189
|
-
echo "✓ Successfully pushed '$current_branch' to origin."
|
|
190
|
-
else
|
|
191
|
-
echo "⚠ Failed to push '$current_branch' to origin."
|
|
192
|
-
return 1
|
|
137
|
+
fi
|
|
138
|
+
|
|
139
|
+
# Keep the local base branch current as well (only if it can fast-forward)
|
|
140
|
+
if git show-ref --verify --quiet "refs/heads/$base_branch" && ! _update_is_checked_out "$base_branch"; then
|
|
141
|
+
git fetch --quiet . "refs/remotes/$remote/$base_branch:refs/heads/$base_branch" 2>/dev/null ||
|
|
142
|
+
print_warning "Local '$base_branch' has its own commits; it was not fast-forwarded."
|
|
143
|
+
fi
|
|
144
|
+
|
|
145
|
+
print_info "Merging '$remote/$base_branch' into '$current_branch'..."
|
|
146
|
+
if git merge --no-edit "$remote/$base_branch"; then
|
|
147
|
+
print_success "'$current_branch' is up to date with '$base_branch'."
|
|
148
|
+
else
|
|
149
|
+
local conflicts
|
|
150
|
+
conflicts=$(git diff --name-only --diff-filter=U)
|
|
151
|
+
if [[ -z "$conflicts" ]]; then
|
|
152
|
+
print_error "Merge failed (see git's message above)."
|
|
153
|
+
if [[ "$stashed" == true ]]; then
|
|
154
|
+
git stash pop --quiet || print_warning "Your changes are still in the stash: run 'git stash pop'."
|
|
193
155
|
fi
|
|
156
|
+
return 1
|
|
194
157
|
fi
|
|
195
|
-
else
|
|
196
|
-
echo "⚠ Merge completed with conflicts. Resolve them manually."
|
|
197
158
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
159
|
+
print_warning "Merge conflicts in:"
|
|
160
|
+
printf '%s\n' "$conflicts" | sed 's/^/ /'
|
|
161
|
+
echo "Resolve them and run 'git commit', or cancel with 'git merge --abort'."
|
|
162
|
+
[[ "$stashed" == true ]] && echo "Your stashed changes are kept: run 'git stash pop' when done."
|
|
163
|
+
|
|
164
|
+
local -a merge_tool
|
|
165
|
+
read -r -a merge_tool <<< "${GITBASH_MERGE_COMMAND:-}"
|
|
166
|
+
if [[ ${#merge_tool[@]} -gt 0 ]] && command -v "${merge_tool[0]}" >/dev/null 2>&1; then
|
|
167
|
+
"${merge_tool[@]}" .
|
|
201
168
|
fi
|
|
169
|
+
return 1
|
|
170
|
+
fi
|
|
171
|
+
|
|
172
|
+
if [[ "$stashed" == true ]]; then
|
|
173
|
+
if git stash pop --quiet; then
|
|
174
|
+
print_info "Restored your stashed changes."
|
|
175
|
+
else
|
|
176
|
+
print_warning "Restoring your changes caused conflicts. They are still in the stash (git stash list)."
|
|
177
|
+
fi
|
|
178
|
+
fi
|
|
179
|
+
|
|
180
|
+
if [[ "$should_push" == true ]]; then
|
|
181
|
+
gb_sync_and_push || return 1
|
|
202
182
|
fi
|
|
203
|
-
|
|
183
|
+
return 0
|
|
184
|
+
}
|
package/docs/CONTRIBUTING.md
CHANGED
|
@@ -109,16 +109,27 @@ When your PR is merged:
|
|
|
109
109
|
|
|
110
110
|
### Testing your changes:
|
|
111
111
|
```bash
|
|
112
|
-
#
|
|
113
|
-
|
|
112
|
+
# Run the test suite (bats, installed with npm install)
|
|
113
|
+
npm test
|
|
114
114
|
|
|
115
|
-
#
|
|
115
|
+
# Run it with a specific bash, e.g. macOS's bash 3.2
|
|
116
|
+
GB_BASH=/bin/bash npm test
|
|
117
|
+
|
|
118
|
+
# Lint
|
|
119
|
+
shellcheck -x bin/gitbash commands/*.sh
|
|
120
|
+
|
|
121
|
+
# Try a command directly
|
|
116
122
|
./bin/gitbash stale --all
|
|
117
123
|
```
|
|
118
124
|
|
|
125
|
+
Tests live in `test/*.bats`. Each test gets its own temporary repository with a local bare remote (`test/helpers/setup.bash`). fzf is replaced by a stub that records its arguments and answers from a plan (`test/helpers/bin/fzf`); `test/fzf_real.bats` drives the real fzf in a pseudo-terminal. Add a test for every bug fix.
|
|
126
|
+
|
|
127
|
+
CI runs shellcheck and the tests on Ubuntu (bash 5) and macOS (bash 3.2 and 5).
|
|
128
|
+
|
|
119
129
|
### Code style:
|
|
120
|
-
- Follow existing bash script conventions
|
|
121
|
-
-
|
|
130
|
+
- Follow existing bash script conventions; scripts must work with bash 3.2
|
|
131
|
+
- Commands run in a bash subprocess started by `bin/gitbash`; use the helpers in `commands/_utils.sh` (`run_fzf`, `gb_confirm`, `gb_sync_and_push`, `gb_base_branch`, ...)
|
|
132
|
+
- shellcheck must pass
|
|
122
133
|
- Keep functions focused and well-documented
|
|
123
134
|
- Include help text for new commands/options
|
|
124
135
|
- Keep the readme updated and as terse as possible
|
package/docs/SECURITY.md
CHANGED
|
@@ -6,10 +6,21 @@ The following versions of this project are currently receiving security updates:
|
|
|
6
6
|
|
|
7
7
|
| Version | Supported |
|
|
8
8
|
|--------|-----------|
|
|
9
|
-
|
|
|
10
|
-
| <
|
|
9
|
+
| 2.x.x | ✅ |
|
|
10
|
+
| < 2.0 | ❌ |
|
|
11
11
|
|
|
12
|
-
Security fixes are only applied to the latest minor/patch release of the **
|
|
12
|
+
Security fixes are only applied to the latest minor/patch release of the **2.x.x** series. Older versions must be upgraded to receive patches.
|
|
13
|
+
|
|
14
|
+
Versions before 2.0 executed repository `.gitbashrc` files as shell code, so opening a cloned repository with a malicious `.gitbashrc` could run arbitrary commands. Upgrade to 2.0 or later.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Trust Model
|
|
19
|
+
|
|
20
|
+
- Configuration files (`~/.gitbashrc`, a repository's `.gitbashrc` and `.gitbashrc-user`) are **parsed, never executed**. Only plain `GITBASH_*="value"` lines from a fixed list of settings are read; values containing `$`, backticks, backslashes or quotes are rejected.
|
|
21
|
+
- A repository's committed `.gitbashrc` is treated as untrusted: it cannot set `GITBASH_MERGE_COMMAND` (the only setting that names a program to run). That setting is only read from `~/.gitbashrc` and `.gitbashrc-user`.
|
|
22
|
+
- `gitbash --init` prints wrapper functions that call the installed `gitbash` binary; command code is not sourced into your shell.
|
|
23
|
+
- `pr` never passes credentials from the remote URL to the browser.
|
|
13
24
|
|
|
14
25
|
---
|
|
15
26
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gitbash",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Opinionated git utilities for bash with fzf-powered menus",
|
|
5
5
|
"author": "jaggli",
|
|
6
6
|
"license": "MIT",
|
|
@@ -41,11 +41,13 @@
|
|
|
41
41
|
"node": ">=14"
|
|
42
42
|
},
|
|
43
43
|
"scripts": {
|
|
44
|
+
"test": "bats test",
|
|
44
45
|
"changeset": "changeset",
|
|
45
46
|
"version": "changeset version",
|
|
46
|
-
"release": "npm run version && VERSION=$(node -p \"require('./package.json').version\") &&
|
|
47
|
+
"release": "npm run version && VERSION=$(node -p \"require('./package.json').version\") && ./bin/gitbash commit --yes -p \"chore: release v$VERSION\" && git tag \"v$VERSION\" && git push origin \"v$VERSION\" && npm publish"
|
|
47
48
|
},
|
|
48
49
|
"devDependencies": {
|
|
49
|
-
"@changesets/cli": "^2.29.8"
|
|
50
|
+
"@changesets/cli": "^2.29.8",
|
|
51
|
+
"bats": "^1.13.0"
|
|
50
52
|
}
|
|
51
53
|
}
|