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/pr.sh
CHANGED
|
@@ -1,11 +1,83 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
# Source common utilities
|
|
4
|
+
SOURCE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
5
|
+
# shellcheck source=_utils.sh
|
|
6
|
+
source "$SOURCE_DIR/_utils.sh"
|
|
7
|
+
|
|
8
|
+
# Convert a remote URL to the repository's web URL (credentials and .git removed).
|
|
9
|
+
# Handles https://[user[:token]@]host/path, ssh://[user@]host[:port]/path and user@host:path.
|
|
10
|
+
_pr_web_base() {
|
|
11
|
+
local url="$1" scheme="https" rest host path
|
|
12
|
+
case "$url" in
|
|
13
|
+
http://*|https://*)
|
|
14
|
+
scheme="${url%%://*}"
|
|
15
|
+
rest="${url#*://}"
|
|
16
|
+
host="${rest%%/*}"
|
|
17
|
+
host="${host##*@}"
|
|
18
|
+
path="${rest#*/}"
|
|
19
|
+
;;
|
|
20
|
+
ssh://*)
|
|
21
|
+
rest="${url#ssh://}"
|
|
22
|
+
host="${rest%%/*}"
|
|
23
|
+
host="${host##*@}"
|
|
24
|
+
host="${host%%:*}"
|
|
25
|
+
path="${rest#*/}"
|
|
26
|
+
;;
|
|
27
|
+
*@*:*)
|
|
28
|
+
host="${url%%:*}"
|
|
29
|
+
host="${host##*@}"
|
|
30
|
+
path="${url#*:}"
|
|
31
|
+
;;
|
|
32
|
+
*)
|
|
33
|
+
return 1
|
|
34
|
+
;;
|
|
35
|
+
esac
|
|
36
|
+
path="${path%/}"
|
|
37
|
+
path="${path%.git}"
|
|
38
|
+
if [[ -z "$host" || -z "$path" || ( -n "$rest" && "$path" == "$rest" ) ]]; then
|
|
39
|
+
return 1
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
# Azure DevOps SSH: ssh.dev.azure.com:v3/org/project/repo
|
|
43
|
+
if [[ "$host" == "ssh.dev.azure.com" && "$path" == v3/*/*/* ]]; then
|
|
44
|
+
local org project repo
|
|
45
|
+
path="${path#v3/}"
|
|
46
|
+
org="${path%%/*}"; path="${path#*/}"
|
|
47
|
+
project="${path%%/*}"; repo="${path#*/}"
|
|
48
|
+
echo "https://dev.azure.com/$org/$project/_git/$repo"
|
|
49
|
+
return 0
|
|
50
|
+
fi
|
|
51
|
+
echo "$scheme://$host/$path"
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
# Web URL for creating a pull/merge request for a branch
|
|
55
|
+
_pr_create_url() {
|
|
56
|
+
local base="$1" branch="$2" encoded
|
|
57
|
+
encoded=$(gb_urlencode "$branch")
|
|
58
|
+
case "$base" in
|
|
59
|
+
*gitlab*)
|
|
60
|
+
echo "$base/-/merge_requests/new?merge_request%5Bsource_branch%5D=$encoded"
|
|
61
|
+
;;
|
|
62
|
+
*bitbucket.org/*)
|
|
63
|
+
echo "$base/pull-requests/new?source=$encoded"
|
|
64
|
+
;;
|
|
65
|
+
*dev.azure.com/*|*visualstudio.com/*)
|
|
66
|
+
echo "$base/pullrequestcreate?sourceRef=$encoded"
|
|
67
|
+
;;
|
|
68
|
+
*)
|
|
69
|
+
echo "$base/compare/$encoded?expand=1"
|
|
70
|
+
;;
|
|
71
|
+
esac
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
# Open the current branch's pull request (or the page to create one)
|
|
3
75
|
pr() {
|
|
4
76
|
local should_push=false
|
|
77
|
+
local print_only=false
|
|
5
78
|
|
|
6
|
-
# Parse arguments
|
|
7
79
|
while [[ $# -gt 0 ]]; do
|
|
8
|
-
case $1 in
|
|
80
|
+
case "$1" in
|
|
9
81
|
-v|--version)
|
|
10
82
|
echo "gitbash ${FUNCNAME[0]} v$VERSION"
|
|
11
83
|
return 0
|
|
@@ -15,36 +87,30 @@ pr() {
|
|
|
15
87
|
Usage: pr [OPTIONS]
|
|
16
88
|
|
|
17
89
|
Open the current branch's pull request in your browser.
|
|
18
|
-
If no
|
|
90
|
+
If no pull request exists yet, opens the page to create one.
|
|
19
91
|
|
|
20
92
|
Options:
|
|
21
|
-
-p, --push Push current branch
|
|
93
|
+
-p, --push Push the current branch before opening
|
|
94
|
+
--print Print the URL instead of opening it
|
|
95
|
+
-y, --yes Don't ask for confirmations
|
|
22
96
|
-h, --help Show this help message
|
|
23
97
|
|
|
24
98
|
Behavior:
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
99
|
+
- Uncommitted changes: offers to commit them first (via 'commit')
|
|
100
|
+
- Branch not on the remote yet: offers to push it
|
|
101
|
+
- With the GitHub CLI (gh) installed and logged in, an existing PR is opened directly
|
|
102
|
+
- Otherwise opens the create page for the hosting service:
|
|
103
|
+
GitHub / GitHub Enterprise <repo>/compare/<branch>?expand=1
|
|
104
|
+
GitLab <repo>/-/merge_requests/new?...
|
|
105
|
+
Bitbucket <repo>/pull-requests/new?source=<branch>
|
|
106
|
+
Azure DevOps <repo>/pullrequestcreate?sourceRef=<branch>
|
|
107
|
+
- Works with SSH and HTTPS remotes; credentials in the remote URL are never used
|
|
108
|
+
- Opens the browser with 'open' (macOS) or 'xdg-open' (Linux), otherwise prints the URL
|
|
29
109
|
|
|
30
110
|
Examples:
|
|
31
111
|
$ pr
|
|
32
|
-
# Opens: https://github.com/user/repo/compare/feature-branch?expand=1
|
|
33
|
-
|
|
34
|
-
$ git checkout feature/helix/LOVE-123-fix-bug
|
|
35
|
-
$ pr
|
|
36
|
-
# Opens: https://github.com/user/repo/compare/feature/helix/LOVE-123-fix-bug?expand=1
|
|
37
|
-
|
|
38
|
-
# Push before opening PR
|
|
39
112
|
$ pr -p
|
|
40
|
-
|
|
41
|
-
✓ Successfully pushed 'feature-branch' to origin.
|
|
42
|
-
# Opens: https://github.com/user/repo/compare/feature-branch?expand=1
|
|
43
|
-
|
|
44
|
-
Notes:
|
|
45
|
-
- Works with both SSH and HTTPS remote URLs
|
|
46
|
-
- Opens in your default browser using the 'open' command
|
|
47
|
-
- Branch name is automatically URL-encoded by the browser
|
|
113
|
+
$ pr --print
|
|
48
114
|
|
|
49
115
|
EOF
|
|
50
116
|
return 0
|
|
@@ -53,146 +119,92 @@ EOF
|
|
|
53
119
|
should_push=true
|
|
54
120
|
shift
|
|
55
121
|
;;
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
122
|
+
--print)
|
|
123
|
+
print_only=true
|
|
124
|
+
shift
|
|
125
|
+
;;
|
|
126
|
+
-y|--yes)
|
|
127
|
+
GITBASH_ASSUME_YES=1
|
|
128
|
+
shift
|
|
60
129
|
;;
|
|
61
130
|
*)
|
|
62
|
-
|
|
63
|
-
echo "Usage: pr [-p|--push]"
|
|
131
|
+
print_error "Unknown argument: $1"
|
|
132
|
+
echo "Usage: pr [-p|--push] [--print] [-y|--yes]" >&2
|
|
64
133
|
return 1
|
|
65
134
|
;;
|
|
66
135
|
esac
|
|
67
136
|
done
|
|
68
137
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
138
|
+
require_git_repo || return 1
|
|
139
|
+
|
|
140
|
+
local remote remote_url branch base
|
|
141
|
+
remote=$(gb_remote)
|
|
142
|
+
if ! remote_url=$(git config --get "remote.$remote.url"); then
|
|
143
|
+
print_error "No remote '$remote' found."
|
|
72
144
|
return 1
|
|
73
145
|
fi
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
146
|
+
if ! branch=$(gb_current_branch); then
|
|
147
|
+
print_error "Detached HEAD: check out a branch first."
|
|
148
|
+
return 1
|
|
149
|
+
fi
|
|
150
|
+
if ! base=$(_pr_web_base "$remote_url"); then
|
|
151
|
+
print_error "Don't know how to open '$remote_url' in a browser."
|
|
152
|
+
return 1
|
|
79
153
|
fi
|
|
80
|
-
|
|
81
|
-
if [[ "$has_changes" == true ]]; then
|
|
82
|
-
echo "You have uncommitted changes or untracked files."
|
|
83
|
-
echo ""
|
|
84
|
-
|
|
85
|
-
# Source _utils.sh for prompt_read (status may use prompts)
|
|
86
|
-
if ! declare -f prompt_read >/dev/null 2>&1; then
|
|
87
|
-
SOURCE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)"
|
|
88
|
-
source "$SOURCE_DIR/_utils.sh"
|
|
89
|
-
fi
|
|
90
154
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
155
|
+
# -----------------------------
|
|
156
|
+
# 1. Uncommitted changes
|
|
157
|
+
# -----------------------------
|
|
158
|
+
if [[ -n "$(git status --porcelain 2>/dev/null)" ]]; then
|
|
159
|
+
echo "You have uncommitted changes or untracked files."
|
|
160
|
+
if [[ "$should_push" == true ]] || gb_confirm "Commit them first?" y; then
|
|
161
|
+
local before after
|
|
162
|
+
before=$(git rev-parse --verify --quiet HEAD || true)
|
|
163
|
+
if ! gb_run commit; then
|
|
164
|
+
print_error "Commit cancelled or failed. Not opening the pull request."
|
|
165
|
+
return 1
|
|
166
|
+
fi
|
|
167
|
+
after=$(git rev-parse --verify --quiet HEAD || true)
|
|
168
|
+
if [[ "$after" != "$before" && "$should_push" == false ]]; then
|
|
169
|
+
gb_confirm "Push the new commit to '$remote'?" y && should_push=true
|
|
170
|
+
fi
|
|
95
171
|
else
|
|
96
|
-
|
|
172
|
+
echo "Continuing without committing..."
|
|
97
173
|
fi
|
|
98
|
-
|
|
99
|
-
case "$commit_answer" in
|
|
100
|
-
[nN][oO]|[nN])
|
|
101
|
-
echo "Continuing without committing..."
|
|
102
|
-
;;
|
|
103
|
-
*)
|
|
104
|
-
# Run status command for interactive staging and committing
|
|
105
|
-
local local_before
|
|
106
|
-
local local_after
|
|
107
|
-
local_before=$(git rev-parse --verify HEAD 2>/dev/null || true)
|
|
108
|
-
if ! gitbash status; then
|
|
109
|
-
echo "Commit cancelled or failed. Aborting PR creation."
|
|
110
|
-
return 1
|
|
111
|
-
fi
|
|
112
|
-
local_after=$(git rev-parse --verify HEAD 2>/dev/null || true)
|
|
113
|
-
|
|
114
|
-
# Only offer to push if a new commit was actually created
|
|
115
|
-
if [[ "$local_after" != "$local_before" ]]; then
|
|
116
|
-
# If -p wasn't specified, offer to push the fresh commit
|
|
117
|
-
if [[ "$should_push" != true ]]; then
|
|
118
|
-
local push_answer
|
|
119
|
-
echo ""
|
|
120
|
-
prompt_read "Push the fresh commit to origin? (Y/n): " push_answer
|
|
121
|
-
case "$push_answer" in
|
|
122
|
-
[nN][oO]|[nN])
|
|
123
|
-
echo "Continuing without pushing..."
|
|
124
|
-
;;
|
|
125
|
-
*)
|
|
126
|
-
should_push=true
|
|
127
|
-
;;
|
|
128
|
-
esac
|
|
129
|
-
fi
|
|
130
|
-
else
|
|
131
|
-
echo "No new commit was created. Continuing without pushing..."
|
|
132
|
-
fi
|
|
133
|
-
;;
|
|
134
|
-
esac
|
|
135
|
-
echo ""
|
|
136
174
|
fi
|
|
137
175
|
|
|
138
|
-
#
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
if [[
|
|
142
|
-
|
|
143
|
-
|
|
176
|
+
# -----------------------------
|
|
177
|
+
# 2. Make sure the branch exists on the remote
|
|
178
|
+
# -----------------------------
|
|
179
|
+
if [[ "$should_push" == false ]]; then
|
|
180
|
+
local on_remote
|
|
181
|
+
on_remote=$(git ls-remote --heads "$remote" "refs/heads/$branch" 2>/dev/null || true)
|
|
182
|
+
if [[ -z "$on_remote" ]]; then
|
|
183
|
+
print_warning "Branch '$branch' is not on '$remote' yet."
|
|
184
|
+
gb_confirm "Push it now?" y && should_push=true
|
|
185
|
+
fi
|
|
144
186
|
fi
|
|
145
187
|
|
|
146
|
-
# Convert SSH URLs to HTTPS URLs
|
|
147
|
-
remote_https=$remote
|
|
148
|
-
remote_https=${remote_https/git@github.com:/https://github.com/}
|
|
149
|
-
remote_https=${remote_https%.git}
|
|
150
|
-
|
|
151
|
-
# Current branch
|
|
152
|
-
branch=$(git rev-parse --abbrev-ref HEAD)
|
|
153
|
-
|
|
154
|
-
# Push if requested
|
|
155
188
|
if [[ "$should_push" == true ]]; then
|
|
156
|
-
|
|
157
|
-
git fetch origin "$branch" 2>/dev/null
|
|
158
|
-
local local_commit
|
|
159
|
-
local remote_commit
|
|
160
|
-
local_commit=$(git rev-parse HEAD)
|
|
161
|
-
remote_commit=$(git rev-parse "origin/$branch" 2>/dev/null)
|
|
162
|
-
|
|
163
|
-
if [[ -n "$remote_commit" && "$local_commit" != "$remote_commit" ]]; then
|
|
164
|
-
# Check if we're behind the remote
|
|
165
|
-
if git merge-base --is-ancestor "$local_commit" "$remote_commit" 2>/dev/null; then
|
|
166
|
-
echo "Remote has updates. Pulling first..."
|
|
167
|
-
if ! git pull --rebase origin "$branch"; then
|
|
168
|
-
echo "⚠ Failed to pull remote changes. Please resolve conflicts and try again."
|
|
169
|
-
return 1
|
|
170
|
-
fi
|
|
171
|
-
echo "✓ Pulled latest changes."
|
|
172
|
-
elif ! git merge-base --is-ancestor "$remote_commit" "$local_commit" 2>/dev/null; then
|
|
173
|
-
# Branches have diverged
|
|
174
|
-
echo "Remote has diverged. Pulling with rebase..."
|
|
175
|
-
git pull --rebase origin "$branch"
|
|
176
|
-
if ! git pull --rebase origin "$branch"; then
|
|
177
|
-
echo "⚠ Failed to pull remote changes. Please resolve conflicts and try again."
|
|
178
|
-
return 1
|
|
179
|
-
fi
|
|
180
|
-
echo "✓ Rebased on latest changes."
|
|
181
|
-
fi
|
|
182
|
-
fi
|
|
183
|
-
|
|
184
|
-
echo "Pushing '$branch' to origin..."
|
|
185
|
-
if git push origin "$branch"; then
|
|
186
|
-
echo "✓ Successfully pushed '$branch' to origin."
|
|
187
|
-
else
|
|
188
|
-
echo "⚠ Failed to push '$branch' to origin."
|
|
189
|
-
return 1
|
|
190
|
-
fi
|
|
189
|
+
gb_sync_and_push || return 1
|
|
191
190
|
fi
|
|
192
191
|
|
|
193
|
-
#
|
|
194
|
-
|
|
192
|
+
# -----------------------------
|
|
193
|
+
# 3. Open existing PR with gh, else the create page
|
|
194
|
+
# -----------------------------
|
|
195
|
+
if [[ "$print_only" == false && "$base" != *gitlab* && "$base" != *bitbucket.org/* ]] &&
|
|
196
|
+
command -v gh >/dev/null 2>&1; then
|
|
197
|
+
if GH_PROMPT_DISABLED=1 gh pr view "$branch" --web >/dev/null 2>&1; then
|
|
198
|
+
print_success "Opened the pull request for '$branch'."
|
|
199
|
+
return 0
|
|
200
|
+
fi
|
|
201
|
+
fi
|
|
195
202
|
|
|
196
|
-
|
|
197
|
-
|
|
203
|
+
local url
|
|
204
|
+
url=$(_pr_create_url "$base" "$branch")
|
|
205
|
+
if [[ "$print_only" == true ]]; then
|
|
206
|
+
echo "$url"
|
|
207
|
+
else
|
|
208
|
+
gb_open_url "$url"
|
|
209
|
+
fi
|
|
198
210
|
}
|