@san-siva/gitsy 1.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.
Files changed (16) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +53 -0
  3. package/g-cb +47 -0
  4. package/g-co +106 -0
  5. package/g-db +122 -0
  6. package/g-diff +228 -0
  7. package/g-dlc +109 -0
  8. package/g-pull +72 -0
  9. package/g-push +67 -0
  10. package/g-rmf +57 -0
  11. package/g-rto +50 -0
  12. package/g-s +50 -0
  13. package/g-wa +155 -0
  14. package/g-wr +260 -0
  15. package/package.json +70 -0
  16. package/utils +464 -0
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "@san-siva/gitsy",
3
+ "version": "1.0.0",
4
+ "description": "A set of bash utilities for managing Git repositories with ease",
5
+ "keywords": [
6
+ "git",
7
+ "cli",
8
+ "bash",
9
+ "git-tools",
10
+ "git-utilities",
11
+ "workflow",
12
+ "productivity"
13
+ ],
14
+ "homepage": "https://gitsy-56895.web.app",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/san-siva/gitsy.git"
18
+ },
19
+ "bugs": {
20
+ "url": "https://github.com/san-siva/gitsy/issues"
21
+ },
22
+ "author": {
23
+ "name": "Santhosh Siva",
24
+ "url": "https://github.com/san-siva"
25
+ },
26
+ "license": "MIT",
27
+ "bin": {
28
+ "g-co": "./g-co",
29
+ "g-pull": "./g-pull",
30
+ "g-push": "./g-push",
31
+ "g-wa": "./g-wa",
32
+ "g-wr": "./g-wr",
33
+ "g-db": "./g-db",
34
+ "g-dlc": "./g-dlc",
35
+ "g-rmf": "./g-rmf",
36
+ "g-rto": "./g-rto",
37
+ "g-cb": "./g-cb",
38
+ "g-s": "./g-s",
39
+ "g-diff": "./g-diff"
40
+ },
41
+ "files": [
42
+ "g-co",
43
+ "g-pull",
44
+ "g-push",
45
+ "g-wa",
46
+ "g-wr",
47
+ "g-db",
48
+ "g-dlc",
49
+ "g-rmf",
50
+ "g-rto",
51
+ "g-cb",
52
+ "g-s",
53
+ "g-diff",
54
+ "utils",
55
+ "README.md",
56
+ "LICENSE"
57
+ ],
58
+ "scripts": {
59
+ "test": "bash -c 'for script in g-*; do bash -n \"$script\" || exit 1; done'",
60
+ "postinstall": "echo '\\n✓ gitsy installed successfully!\\n\\nDependencies required: git, figlet, lolcat\\n\\nRun any command with --help to get started:\\n g-co --help\\n\\nDocumentation: https://gitsy-56895.web.app\\n'"
61
+ },
62
+ "engines": {
63
+ "node": ">=12.0.0"
64
+ },
65
+ "os": [
66
+ "darwin",
67
+ "linux"
68
+ ],
69
+ "preferGlobal": true
70
+ }
package/utils ADDED
@@ -0,0 +1,464 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # Author: Santhosh Siva
4
+ # Date Created: 03-08-2025
5
+
6
+ # Colors
7
+ BLUE=$(tput setaf 4)
8
+ PROMPT=$(tput setaf 3)
9
+ GREEN=$(tput setaf 2)
10
+ RED=$(tput setaf 1)
11
+ NC=$(tput sgr0)
12
+
13
+ overwrite() { echo -e "\r\033[1A\033[0K$@"; }
14
+
15
+ default_spaces=" "
16
+
17
+ print_message() {
18
+ local number=$2
19
+ local message=$1
20
+
21
+ if [ -z "$message" ]; then
22
+ message=""
23
+ fi
24
+
25
+ if [ -z "$number" ]; then
26
+ number=0
27
+ fi
28
+
29
+ if [ "$number" -eq 0 ]; then
30
+ echo -e " $message"
31
+ return 0
32
+ fi
33
+
34
+ # print_message ""
35
+
36
+ if [ "$number" -lt 10 ]; then
37
+ printf "%d. %s\n" "$number" "$message"
38
+ return 0
39
+ fi
40
+
41
+ printf "%02d. %s\n" "$number" "$message"
42
+ }
43
+
44
+ indent() {
45
+ local prefix=" │ "
46
+ sed "s/^/${prefix}/"
47
+ }
48
+
49
+ install_dependency() {
50
+ local cmd=$1
51
+ local package=$2
52
+
53
+ if ! command -v "$cmd" >/dev/null; then
54
+ if brew install "$package" >>"$log_file" 2>>"$error_log_file"; then
55
+ return
56
+ else
57
+ print_message "${RED}Failed to install $package.${NC}"
58
+ exit 1
59
+ fi
60
+ fi
61
+ }
62
+
63
+ validate_dependencies() {
64
+ for cmd in $@; do
65
+ install_dependency "$cmd" "$cmd"
66
+ done
67
+ }
68
+
69
+ print_banner() {
70
+ print_message ""
71
+ figlet -f slant "Gitsy" | lolcat
72
+ print_message ""
73
+ }
74
+
75
+ prompt_user() {
76
+ local default_to_yes=$1
77
+ local message=$2
78
+ local step_number=$3
79
+
80
+ if [ -n "$step_number" ] && [ "$step_number" -ne 0 ]; then
81
+ local prefix="$(printf "%${default_spaces}s")${step_number}. "
82
+ elif [ "$step_number" -eq 0 ]; then
83
+ local prefix="$(printf "%${default_spaces}s") "
84
+ else
85
+ local prefix="$(printf "%${default_spaces}s")- "
86
+ fi
87
+
88
+ local prompt="${prefix}${PROMPT}${message}${NC} "
89
+
90
+ if [ "$default_to_yes" = "true" ]; then
91
+ read -p "${prompt}(Y/n): " response
92
+ response="${response:-y}"
93
+ else
94
+ read -p "${prompt}(y/N): " response
95
+ response="${response:-n}"
96
+ fi
97
+
98
+ case "$response" in
99
+ [Yy]) echo "y" ;;
100
+ [Nn]) echo "n" ;;
101
+ *) print_message "Invalid input." 0 && exit 1 ;;
102
+ esac
103
+ }
104
+
105
+ fetch_current_branch() {
106
+ current_branch=$(git rev-parse --abbrev-ref HEAD)
107
+ if [ -z "$current_branch" ]; then
108
+ print_message "${RED}Failed to get current branch. [Fail]${NC}"
109
+ exit 1
110
+ fi
111
+ echo "${current_branch}"
112
+ }
113
+
114
+ stash_changes() {
115
+ local stash_changes=$1
116
+ local step_number=$2
117
+ local tag_message=$3
118
+
119
+ if [ -z "$step_number" ]; then
120
+ step_number=0
121
+ fi
122
+
123
+ if [ "$stash_changes" = "true" ]; then
124
+ print_message "${BLUE}Stashing changes...${NC}" $step_number
125
+
126
+ if ! (git -c color.ui=always add -A 2>&1 | indent); then
127
+ print_message "${RED}Failed to add changes to stash. [Fail]${NC}" 0
128
+ exit 1
129
+ fi
130
+
131
+ local stash_message
132
+ local branch_name=$(git rev-parse --abbrev-ref HEAD)
133
+ local stash_date=$(date '+%Y-%m-%d %H:%M:%S')
134
+
135
+ if [ -n "$tag_message" ]; then
136
+ stash_message="Manual stash; Branch: ${branch_name}; Date: ${stash_date}; Message: ${tag_message}"
137
+ else
138
+ stash_message="Auto stash; Branch: ${branch_name}; Date: ${stash_date}"
139
+ fi
140
+
141
+ if ! (git -c color.ui=always stash push -m "$stash_message" 2>&1 | indent 4); then
142
+ print_message "${RED}Failed to stash changes. [Fail]${NC}" 0
143
+ exit 1
144
+ fi
145
+
146
+ print_message "${GREEN}Changes stashed successfully.${NC}" 0
147
+ fi
148
+ }
149
+
150
+ fetch_changes() {
151
+ local target_branch=$1
152
+
153
+ if [ -z "$target_branch" ]; then
154
+ print_message "${RED}Target branch is not set. [Fail]${NC}" 0
155
+ return 1
156
+ fi
157
+
158
+ if ! git -c color.ui=always fetch origin "${target_branch}" 2>&1 | indent; then
159
+ print_message "${PROMPT}Auto-Fetch failed, Please do it manually.${NC}"
160
+ return 1
161
+ else
162
+ return 0
163
+ fi
164
+
165
+ return 0
166
+ }
167
+
168
+ checkout_branch() {
169
+ local target_branch=$1
170
+ local new_branch=$2
171
+
172
+ if [ -z "$target_branch" ]; then
173
+ print_message "${RED}Target branch is not set. [Fail]${NC}"
174
+ exit 1
175
+ fi
176
+
177
+ if [ "$new_branch" = "true" ]; then
178
+ if ! git -c color.ui=always checkout -b "${target_branch}" 2>&1 | indent; then
179
+ print_message "${RED}Failed to create new branch. [Fail]${NC}"
180
+ exit 1
181
+ fi
182
+ print_message "${GREEN}Created new local branch ${NC}${target_branch}${GREEN}. [DONE]${NC}"
183
+ return 0
184
+ fi
185
+
186
+ if ! git -c color.ui=always checkout "${target_branch}" 2>&1 | indent; then
187
+ print_message "${RED}Failed to checkout to branch ${NC}${target_branch}${RED}. [Fail]${NC}"
188
+ exit 1
189
+ fi
190
+ return 0
191
+ }
192
+
193
+ create_worktree() {
194
+ local target_branch="$1"
195
+ local worktree_path="$2"
196
+ local new_branch="$3"
197
+ local base_branch
198
+ local created_worktree_message="${GREEN}Successfully created worktree for branch ${NC}$target_branch${GREEN} at ${NC}$worktree_path${GREEN}. [DONE]${NC}"
199
+ local failed_worktree_message="${RED}Failed to create worktree for branch ${NC}$target_branch${RED}. [Fail]${NC}"
200
+
201
+ if [ -z "$target_branch" ]; then
202
+ print_message "${RED}Target branch is not set. [Fail]${NC}"
203
+ exit 1
204
+ fi
205
+ if [ -z "$worktree_path" ]; then
206
+ print_message "${RED}Worktree path is not set. [Fail]${NC}"
207
+ exit 1
208
+ fi
209
+ if [ -d "$worktree_path" ]; then
210
+ print_message "${RED}Worktree path already exists. [Fail]${NC}"
211
+ exit 1
212
+ fi
213
+
214
+ base_branch=$(fetch_current_branch)
215
+ if [ -z "$base_branch" ]; then
216
+ print_message "${RED}Failed to get current branch. [Fail]${NC}"
217
+ exit 1
218
+ fi
219
+
220
+ if [ "$new_branch" = "true" ]; then
221
+ # Make sure the source branch exists remotely!
222
+ if ! git rev-parse --verify "origin/$base_branch" >/dev/null 2>&1; then
223
+ print_message "${RED}Base branch ${NC}origin/$base_branch${RED} not found. Cannot create new branch.${NC}"
224
+ exit 1
225
+ fi
226
+
227
+ # Use the -b flag on worktree directly:
228
+ local worktree_output
229
+ worktree_output=$(git -c color.ui=always worktree add -b "$target_branch" "$worktree_path" "origin/$base_branch" 2>&1)
230
+ local worktree_exit_code=$?
231
+
232
+ echo "$worktree_output" | indent
233
+
234
+ if [ $worktree_exit_code -ne 0 ]; then
235
+ print_message "${failed_worktree_message}"
236
+ exit 1
237
+ fi
238
+
239
+ print_message "${BLUE}Pushing new branch ${NC}$target_branch${BLUE} to remote...${NC}"
240
+ if ! git -c color.ui=always push -u origin "$target_branch" 2>&1 | indent; then
241
+ print_message "${RED}Failed to push new branch ${NC}$target_branch${RED} to remote. [Fail]${NC}"
242
+ exit 1
243
+ fi
244
+
245
+ print_message "${created_worktree_message}"
246
+ return 0
247
+ fi
248
+
249
+ # Just add the worktree to an existing branch:
250
+ local worktree_output
251
+ worktree_output=$(git -c color.ui=always worktree add "$worktree_path" "$target_branch" 2>&1)
252
+ local worktree_exit_code=$?
253
+
254
+ echo "$worktree_output" | indent
255
+
256
+ if [ $worktree_exit_code -ne 0 ]; then
257
+ print_message "${failed_worktree_message}"
258
+ exit 1
259
+ fi
260
+
261
+ print_message "${created_worktree_message}"
262
+ return 0
263
+ }
264
+
265
+ reset_to_target_branch() {
266
+ local target_branch=$1
267
+ local step_number=$2
268
+
269
+ if [ -z "$step_number" ]; then
270
+ step_number=0
271
+ fi
272
+
273
+ if [ -z "$target_branch" ]; then
274
+ print_message "${RED}Target branch is not set. [Fail]${NC}"
275
+ exit 1
276
+ fi
277
+
278
+ print_message "${BLUE}Resetting to ${NC}origin/${target_branch}${BLUE}...${NC}" $step_number
279
+
280
+ if ! git -c color.ui=always reset --hard "origin/${target_branch}" 2>&1 | indent; then
281
+ print_message "${RED}Failed to reset to ${NC}origin/${target_branch}${RED}. [Fail]${NC}"
282
+ exit 1
283
+ fi
284
+
285
+ print_message "${GREEN}Reset to ${NC}origin/${target_branch}${GREEN} successfully.${NC}"
286
+ }
287
+
288
+ pull_changes() {
289
+ local target_branch=$1
290
+ local step_number=$2
291
+
292
+ if [ -z "$step_number" ]; then
293
+ step_number=0
294
+ fi
295
+
296
+ if [ -z "$target_branch" ]; then
297
+ print_message "${RED}Target branch is not set. [Fail]${NC}"
298
+ exit 1
299
+ fi
300
+
301
+ print_message "${BLUE}Pulling changes from ${NC}remote/${target_branch}${BLUE}...${NC}" $step_number
302
+ if ! git -c color.ui=always pull origin "${target_branch}" 2>&1 | indent; then
303
+ print_message "${RED}Failed to pull changes from remote. [Fail]${NC}"
304
+ exit 1
305
+ fi
306
+ print_message "${GREEN}Pulled changes from ${NC}remote/${target_branch} ${GREEN}successfully.${NC}"
307
+ }
308
+
309
+ push_changes() {
310
+ local target_branch=$1
311
+ local should_force_push=$2
312
+ local step_number=$3
313
+
314
+ if [ -z "$step_number" ]; then
315
+ step_number=0
316
+ fi
317
+
318
+ if [ -z "$target_branch" ]; then
319
+ print_message "${RED}Target branch is not set. [Fail]${NC}"
320
+ exit 1
321
+ fi
322
+
323
+ # Check if local branch is behind remote
324
+ if [ "$should_force_push" = "false" ]; then
325
+ fetch_changes "${target_branch}" >/dev/null 2>&1
326
+ local local_commit=$(git rev-parse "${target_branch}" 2>/dev/null)
327
+ local remote_commit=$(git rev-parse "origin/${target_branch}" 2>/dev/null)
328
+
329
+ if [ -n "$local_commit" ] && [ -n "$remote_commit" ] && [ "$local_commit" != "$remote_commit" ]; then
330
+ if ! git merge-base --is-ancestor "origin/${target_branch}" "${target_branch}" 2>/dev/null; then
331
+ print_message "${RED}Error: Your local branch is behind or has diverged from remote.${NC}" 0
332
+ print_message "${RED}Use ${NC}--force${RED} flag if you want to force push.${NC}" 0
333
+ exit 1
334
+ fi
335
+ fi
336
+ fi
337
+
338
+ if [ "$should_force_push" = "true" ]; then
339
+ print_message "${BLUE}Force pushing changes to ${NC}remote/${target_branch}${BLUE}...${NC}" $step_number
340
+ if ! git -c color.ui=always push --force origin "${target_branch}" 2>&1 | indent; then
341
+ print_message "${RED}Failed to force push changes to remote. [Fail]${NC}"
342
+ exit 1
343
+ fi
344
+ else
345
+ print_message "${BLUE}Pushing changes to ${NC}remote/${target_branch}${BLUE}...${NC}" $step_number
346
+ if ! git -c color.ui=always push origin "${target_branch}" 2>&1 | indent; then
347
+ print_message "${RED}Failed to push changes to remote. [Fail]${NC}"
348
+ exit 1
349
+ fi
350
+ fi
351
+ print_message "${GREEN}Pushed changes to ${NC}remote/${target_branch} ${GREEN}successfully.${NC}"
352
+ }
353
+
354
+ already_on_branch() {
355
+ local target_branch=$1
356
+ print_message "${BLUE}Checking current branch...${NC}" 1
357
+ current_branch=$(fetch_current_branch true)
358
+ if [ "${target_branch}" = "${current_branch}" ]; then
359
+ print_message "${GREEN}Already on branch ${NC}${target_branch}${GREEN}. [DONE]${NC}"
360
+ exit 1
361
+ fi
362
+ }
363
+
364
+ check_if_target_branch_is_set() {
365
+ local target_branch=$1
366
+ if [ -z "${target_branch}" ]; then
367
+ print_message "${RED}Error: No target branch specified, use -t or --target-branch option.${NC}"
368
+ exit 1
369
+ fi
370
+ }
371
+
372
+ navigate_to_dir() {
373
+ local dir=$1
374
+ if pwd | grep -q "$dir"; then
375
+ return 0
376
+ fi
377
+ if ! cd "$dir" 2>/dev/null; then
378
+ return 1
379
+ fi
380
+ return 0
381
+ }
382
+
383
+ check_uncommitted_changes() {
384
+ if ! git diff-index --quiet HEAD --; then
385
+ return 1
386
+ fi
387
+ return 0
388
+ }
389
+
390
+ is_git_repo() {
391
+ local dir=$1
392
+ local original_path=$PWD
393
+
394
+ if ! navigate_to_dir "$dir"; then
395
+ print_message "${RED}Failed to navigate to directory: ${NC}${dir}${RED}. [Fail]${NC}" 0
396
+ exit 1
397
+ fi
398
+
399
+ if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
400
+ navigate_to_dir "$original_path"
401
+ return 0
402
+ fi
403
+
404
+ navigate_to_dir "$original_path"
405
+ return 1
406
+ }
407
+
408
+ has_uncommitted_changes() {
409
+ local original_path=$PWD
410
+ local worktree_path=$1
411
+
412
+ if ! navigate_to_dir "$worktree_path"; then
413
+ print_message "${RED}Failed to navigate to worktree directory: ${NC}${worktree_path}${RED}. [Fail]${NC}" 0
414
+ exit 1
415
+ fi
416
+
417
+ if ! is_git_repo "$worktree_path"; then
418
+ return 1
419
+ fi
420
+
421
+ if ! check_uncommitted_changes; then
422
+ if ! navigate_to_dir "$original_path"; then
423
+ print_message "${RED}Failed to navigate to original directory: ${NC}${original_path}${RED}. [Fail]${NC}" 0
424
+ exit 1
425
+ fi
426
+ return 0
427
+ fi
428
+
429
+ if ! navigate_to_dir "$original_path"; then
430
+ print_message "${RED}Failed to navigate to original directory: ${NC}${original_path}${RED}. [Fail]${NC}" 0
431
+ exit 1
432
+ fi
433
+ return 1
434
+ }
435
+
436
+ branch_exists_locally() {
437
+ local branch=$1
438
+ if git show-ref --verify --quiet "refs/heads/${branch}"; then
439
+ return 0
440
+ fi
441
+ return 1
442
+ }
443
+
444
+ branch_exists_on_remote() {
445
+ local branch=$1
446
+ if git ls-remote --heads origin "${branch}" | grep -q "${branch}"; then
447
+ return 0
448
+ fi
449
+ return 1
450
+ }
451
+
452
+ copy_to_clipboard() {
453
+ local content="$1"
454
+ local message="$2"
455
+
456
+ if [ -n "$content" ]; then
457
+ # Strip all ANSI escape sequences: colors, character sets, etc.
458
+ printf '%s' "$content" | sed -E $'s/\033(\\[[0-9;]*[a-zA-Z]|\\([0-9A-B])//g' | pbcopy
459
+ fi
460
+
461
+ if [ -n "$message" ]; then
462
+ print_message "${GREEN}${message}${NC}"
463
+ fi
464
+ }