guito 0.2.1 → 0.3.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/QUICK_START.md +223 -0
- package/README.md +4 -0
- package/bin/index.js +400 -50
- package/bin/ui/assets/abap-DLDM7-KI.js +1 -0
- package/bin/ui/assets/apex-DNDY2TF8.js +1 -0
- package/bin/ui/assets/azcli-Y6nb8tq_.js +1 -0
- package/bin/ui/assets/bat-BwHxbl9M.js +1 -0
- package/bin/ui/assets/bicep-CFznDFnq.js +2 -0
- package/bin/ui/assets/cameligo-Bf6VGUru.js +1 -0
- package/bin/ui/assets/clojure-Dnu-v4kV.js +1 -0
- package/bin/ui/assets/codicon-ngg6Pgfi.ttf +0 -0
- package/bin/ui/assets/coffee-Bd8akH9Z.js +1 -0
- package/bin/ui/assets/cpp-BbWJElDN.js +1 -0
- package/bin/ui/assets/csharp-Co3qMtFm.js +1 -0
- package/bin/ui/assets/csp-D-4FJmMZ.js +1 -0
- package/bin/ui/assets/css-DdJfP1eB.js +3 -0
- package/bin/ui/assets/css.worker-BM8aVs8W.js +93 -0
- package/bin/ui/assets/cssMode-D6UF0pvT.js +1 -0
- package/bin/ui/assets/cypher-cTPe9QuQ.js +1 -0
- package/bin/ui/assets/dart-BOtBlQCF.js +1 -0
- package/bin/ui/assets/dockerfile-BG73LgW2.js +1 -0
- package/bin/ui/assets/ecl-BEgZUVRK.js +1 -0
- package/bin/ui/assets/elixir-BkW5O-1t.js +1 -0
- package/bin/ui/assets/flow9-BeJ5waoc.js +1 -0
- package/bin/ui/assets/freemarker2-CuDVhtVe.js +3 -0
- package/bin/ui/assets/fsharp-PahG7c26.js +1 -0
- package/bin/ui/assets/go-acbASCJo.js +1 -0
- package/bin/ui/assets/graphql-BxJiqAUM.js +1 -0
- package/bin/ui/assets/handlebars-BT9qXX0t.js +1 -0
- package/bin/ui/assets/hcl-DtV1sZF8.js +1 -0
- package/bin/ui/assets/html-uO5f1agE.js +1 -0
- package/bin/ui/assets/html.worker-CtwWtMPk.js +470 -0
- package/bin/ui/assets/htmlMode-4Oyo7HnK.js +1 -0
- package/bin/ui/assets/index-B2cKC_ky.js +1634 -0
- package/bin/ui/assets/index-tfc2bLEr.css +1 -0
- package/bin/ui/assets/ini-Kd9XrMLS.js +1 -0
- package/bin/ui/assets/java-CXBNlu9o.js +1 -0
- package/bin/ui/assets/javascript-MVQT3ZBU.js +1 -0
- package/bin/ui/assets/json.worker-BfEDn18l.js +58 -0
- package/bin/ui/assets/jsonMode-BkwogsZa.js +7 -0
- package/bin/ui/assets/julia-cl7-CwDS.js +1 -0
- package/bin/ui/assets/kotlin-s7OhZKlX.js +1 -0
- package/bin/ui/assets/less-9HpZscsL.js +2 -0
- package/bin/ui/assets/lexon-OrD6JF1K.js +1 -0
- package/bin/ui/assets/liquid-C7__O8so.js +1 -0
- package/bin/ui/assets/lspLanguageFeatures-CjaZl6Ei.js +4 -0
- package/bin/ui/assets/lua-Cyyb5UIc.js +1 -0
- package/bin/ui/assets/m3-B8OfTtLu.js +1 -0
- package/bin/ui/assets/markdown-BFxVWTOG.js +1 -0
- package/bin/ui/assets/mdx-DpX13nwZ.js +1 -0
- package/bin/ui/assets/mips-CiqrrVzr.js +1 -0
- package/bin/ui/assets/msdax-DmeGPVcC.js +1 -0
- package/bin/ui/assets/mysql-C_tMU-Nz.js +1 -0
- package/bin/ui/assets/objective-c-BDtDVThU.js +1 -0
- package/bin/ui/assets/pascal-vHIfCaH5.js +1 -0
- package/bin/ui/assets/pascaligo-DtZ0uQbO.js +1 -0
- package/bin/ui/assets/perl-Ub6l9XKa.js +1 -0
- package/bin/ui/assets/pgsql-BlNEE0v7.js +1 -0
- package/bin/ui/assets/php-BBUBE1dy.js +1 -0
- package/bin/ui/assets/pla-DSh2-awV.js +1 -0
- package/bin/ui/assets/postiats-CocnycG-.js +1 -0
- package/bin/ui/assets/powerquery-tScXyioY.js +1 -0
- package/bin/ui/assets/powershell-COWaemsV.js +1 -0
- package/bin/ui/assets/protobuf-Brw8urJB.js +2 -0
- package/bin/ui/assets/pug-8SOpv6rk.js +1 -0
- package/bin/ui/assets/python-DSbw_Utg.js +1 -0
- package/bin/ui/assets/qsharp-Bw9ernYp.js +1 -0
- package/bin/ui/assets/r-j7ic8hl3.js +1 -0
- package/bin/ui/assets/razor-BrjmxdA-.js +1 -0
- package/bin/ui/assets/redis-Bu5POkcn.js +1 -0
- package/bin/ui/assets/redshift-Bs9aos_-.js +1 -0
- package/bin/ui/assets/restructuredtext-CqXO7rUv.js +1 -0
- package/bin/ui/assets/ruby-zBfavPgS.js +1 -0
- package/bin/ui/assets/rust-BzKRNQWT.js +1 -0
- package/bin/ui/assets/sb-BBc9UKZt.js +1 -0
- package/bin/ui/assets/scala-D9hQfWCl.js +1 -0
- package/bin/ui/assets/scheme-BPhDTwHR.js +1 -0
- package/bin/ui/assets/scss-CBJaRo0y.js +3 -0
- package/bin/ui/assets/shell-DiJ1NA_G.js +1 -0
- package/bin/ui/assets/solidity-Db0IVjzk.js +1 -0
- package/bin/ui/assets/sophia-CnS9iZB_.js +1 -0
- package/bin/ui/assets/sparql-CJmd_6j2.js +1 -0
- package/bin/ui/assets/sql-ClhHkBeG.js +1 -0
- package/bin/ui/assets/st-CHwy0fLd.js +1 -0
- package/bin/ui/assets/svelte-CKrU6DOB.css +18683 -18683
- package/bin/ui/assets/svelte-dark-7cNU_wYW.css +18683 -18683
- package/bin/ui/assets/swift-Bqt4WxQ4.js +3 -0
- package/bin/ui/assets/systemverilog-Bs9z6M-B.js +1 -0
- package/bin/ui/assets/tcl-Dm6ycUr_.js +1 -0
- package/bin/ui/assets/ts.worker-a4tsX_kw.js +67731 -0
- package/bin/ui/assets/tsMode-CFWjBB2T.js +11 -0
- package/bin/ui/assets/twig-Csy3S7wG.js +1 -0
- package/bin/ui/assets/typescript-CgpPokS_.js +1 -0
- package/bin/ui/assets/typespec-Btyra-wh.js +1 -0
- package/bin/ui/assets/vb-Db0cS2oM.js +1 -0
- package/bin/ui/assets/wgsl-BTesnYfV.js +298 -0
- package/bin/ui/assets/xml-tZACxwqM.js +1 -0
- package/bin/ui/assets/yaml-JpDON7Aq.js +1 -0
- package/bin/ui/index.html +3 -3
- package/package.json +7 -1
- package/bin/ui/assets/index-6fEMLduw.js +0 -7876
- package/bin/ui/assets/index-baVP32pl.css +0 -1
package/QUICK_START.md
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
# Guito - Quick Reference Guide
|
|
2
|
+
|
|
3
|
+
## Getting Started
|
|
4
|
+
|
|
5
|
+
### Launch the App
|
|
6
|
+
```bash
|
|
7
|
+
npm run dev # Development with hot reload
|
|
8
|
+
npm run build:start # Build and start production
|
|
9
|
+
npm start # Start pre-built server
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
The app opens automatically in your browser at `http://localhost:8080`
|
|
13
|
+
|
|
14
|
+
## Features by Category
|
|
15
|
+
|
|
16
|
+
### 📝 Staging & Commits
|
|
17
|
+
**Header Icon:** Check Circle
|
|
18
|
+
|
|
19
|
+
1. **Stage/Unstage Files**
|
|
20
|
+
- Click "Stage/Unstage Files" button
|
|
21
|
+
- Select files to stage or unstage
|
|
22
|
+
- Supports partial file staging via patch mode
|
|
23
|
+
|
|
24
|
+
2. **Create Commit**
|
|
25
|
+
- Click "Commit" button
|
|
26
|
+
- Enter commit message (required)
|
|
27
|
+
- Add optional description
|
|
28
|
+
- Click "Commit" to save
|
|
29
|
+
|
|
30
|
+
3. **Amend Last Commit**
|
|
31
|
+
- Click "Amend" button
|
|
32
|
+
- Modify the message/description
|
|
33
|
+
- Changes are applied to the last commit
|
|
34
|
+
|
|
35
|
+
4. **Revert Commit**
|
|
36
|
+
- Click "Revert" button
|
|
37
|
+
- Enter commit hash to revert
|
|
38
|
+
- Creates new commit that undoes changes
|
|
39
|
+
|
|
40
|
+
### 🌿 Branch Management
|
|
41
|
+
**Header Icon:** Fork
|
|
42
|
+
|
|
43
|
+
1. **View & Switch Branches**
|
|
44
|
+
- Click "Branches" button
|
|
45
|
+
- Current branch highlighted in blue
|
|
46
|
+
- Click "Checkout" to switch branches
|
|
47
|
+
- Click delete icon to remove branches
|
|
48
|
+
- Click "New Branch" to create
|
|
49
|
+
|
|
50
|
+
2. **Create Branch**
|
|
51
|
+
- Click "New Branch" in Branches dialog
|
|
52
|
+
- Enter branch name
|
|
53
|
+
- Branches from current commit
|
|
54
|
+
|
|
55
|
+
3. **Merge Branch**
|
|
56
|
+
- Click "Merge" button
|
|
57
|
+
- Enter branch name to merge
|
|
58
|
+
- Current branch gets merged changes
|
|
59
|
+
|
|
60
|
+
4. **Rebase Branch**
|
|
61
|
+
- Click "Rebase" button
|
|
62
|
+
- Enter branch to rebase onto
|
|
63
|
+
- Replays current commits on target branch
|
|
64
|
+
|
|
65
|
+
### 📦 Stash
|
|
66
|
+
**Header Icon:** Inventory Box
|
|
67
|
+
|
|
68
|
+
1. **Create Stash**
|
|
69
|
+
- Click "Stash" button
|
|
70
|
+
- Enter description for changes
|
|
71
|
+
- Click "Save"
|
|
72
|
+
- Working directory is cleaned
|
|
73
|
+
|
|
74
|
+
2. **Restore from Stash**
|
|
75
|
+
- Click "Stash" button
|
|
76
|
+
- Click "Apply" to keep stash or "Pop" to remove it
|
|
77
|
+
- Changes are restored to working directory
|
|
78
|
+
|
|
79
|
+
3. **Preview Stash**
|
|
80
|
+
- Click "Stash" button
|
|
81
|
+
- Click eye icon to see what's in stash
|
|
82
|
+
- Shows file diffs without applying
|
|
83
|
+
|
|
84
|
+
4. **Delete Stash**
|
|
85
|
+
- Click "Stash" button
|
|
86
|
+
- Click delete icon to permanently remove
|
|
87
|
+
|
|
88
|
+
### 🏷️ Tags
|
|
89
|
+
**Header Icon:** Label
|
|
90
|
+
|
|
91
|
+
1. **Create Tag**
|
|
92
|
+
- Click "Tags" button
|
|
93
|
+
- Enter tag name
|
|
94
|
+
- Optionally check "Annotated tag" for message
|
|
95
|
+
- Click "Create Tag"
|
|
96
|
+
|
|
97
|
+
2. **View Tags**
|
|
98
|
+
- Click "Tags" button
|
|
99
|
+
- All tags listed with delete option
|
|
100
|
+
- Tags mark specific commits for releases
|
|
101
|
+
|
|
102
|
+
3. **Delete Tag**
|
|
103
|
+
- Click "Tags" button
|
|
104
|
+
- Click delete icon next to tag name
|
|
105
|
+
|
|
106
|
+
### 🔄 Remote Operations
|
|
107
|
+
**Header Icons:** Cloud icons (Fetch, Pull, Push)
|
|
108
|
+
|
|
109
|
+
1. **Fetch from Remote**
|
|
110
|
+
- Click "Fetch" button
|
|
111
|
+
- Updates remote tracking branches
|
|
112
|
+
- Safe operation - no local changes
|
|
113
|
+
|
|
114
|
+
2. **Pull from Remote**
|
|
115
|
+
- Click "Pull" button
|
|
116
|
+
- Fetches and merges remote changes
|
|
117
|
+
- Equivalent to fetch + merge
|
|
118
|
+
|
|
119
|
+
3. **Push to Remote**
|
|
120
|
+
- Click "Push" button
|
|
121
|
+
- Sends local commits to remote
|
|
122
|
+
- Updates remote branch
|
|
123
|
+
|
|
124
|
+
## Header Layout
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
[Menu] Guito Branch: main
|
|
128
|
+
|
|
129
|
+
[Staging] [Commits] [Branches] [Stash/Tags] [Remote]
|
|
130
|
+
📋 📝 📝(Amend) ↩️ 🌿 🔀 ↻ 📦 🏷️ ☁️↓ ☁️↑ ☁️↔️
|
|
131
|
+
Stage Commit Amend Revert Branches Merge Rebase Stash Tags Fetch Pull Push
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Keyboard Tips
|
|
135
|
+
|
|
136
|
+
- **Commit Dialog**: Enter to submit (with valid message)
|
|
137
|
+
- **Selection**: Click checkboxes to select files
|
|
138
|
+
- **Dialogs**: Escape to close (on most)
|
|
139
|
+
|
|
140
|
+
## Error Handling
|
|
141
|
+
|
|
142
|
+
All operations show error messages if something goes wrong:
|
|
143
|
+
- Missing required info (message, branch names)
|
|
144
|
+
- Git command failures
|
|
145
|
+
- Network issues with remote operations
|
|
146
|
+
|
|
147
|
+
## Common Workflows
|
|
148
|
+
|
|
149
|
+
### Create & Commit Work
|
|
150
|
+
1. Make changes to files
|
|
151
|
+
2. Click "Stage/Unstage Files"
|
|
152
|
+
3. Select files you want to commit
|
|
153
|
+
4. Click "Commit"
|
|
154
|
+
5. Enter message and click "Commit"
|
|
155
|
+
|
|
156
|
+
### Create a Feature Branch
|
|
157
|
+
1. Click "Branches"
|
|
158
|
+
2. Click "New Branch"
|
|
159
|
+
3. Enter branch name (e.g., "feature/new-ui")
|
|
160
|
+
4. Click "Create Branch"
|
|
161
|
+
|
|
162
|
+
### Save Work Temporarily
|
|
163
|
+
1. Click "Stash"
|
|
164
|
+
2. Enter description
|
|
165
|
+
3. Click "Save"
|
|
166
|
+
4. Switch to another branch or pull
|
|
167
|
+
5. Come back and click "Pop" to restore
|
|
168
|
+
|
|
169
|
+
### Release with Tag
|
|
170
|
+
1. Make sure commits are pushed
|
|
171
|
+
2. Click "Tags"
|
|
172
|
+
3. Enter tag name (e.g., "v1.0.0")
|
|
173
|
+
4. Check "Annotated tag" for release notes
|
|
174
|
+
5. Click "Create Tag"
|
|
175
|
+
|
|
176
|
+
### Merge Completed Feature
|
|
177
|
+
1. Click "Branches"
|
|
178
|
+
2. Checkout main/master branch
|
|
179
|
+
3. Go back to header
|
|
180
|
+
4. Click "Merge"
|
|
181
|
+
5. Enter feature branch name
|
|
182
|
+
6. Resolve any conflicts if needed
|
|
183
|
+
|
|
184
|
+
## Status Indicators
|
|
185
|
+
|
|
186
|
+
- **Branch Name (Blue, Bold)**: Currently checked out branch
|
|
187
|
+
- **Green Files**: Staged files ready to commit
|
|
188
|
+
- **(current)**: Shows which branch you're on
|
|
189
|
+
- **Preview**: Shows what changes are in a stash
|
|
190
|
+
|
|
191
|
+
## Tips & Tricks
|
|
192
|
+
|
|
193
|
+
- Always review what you're staging before committing
|
|
194
|
+
- Use stash to save incomplete work before switching branches
|
|
195
|
+
- Annotated tags with messages are better for releases
|
|
196
|
+
- Fetch regularly to stay updated with remote changes
|
|
197
|
+
- Use descriptive commit messages and branch names
|
|
198
|
+
|
|
199
|
+
## Troubleshooting
|
|
200
|
+
|
|
201
|
+
**"Checkout failed"**
|
|
202
|
+
- Ensure working directory is clean (stage/stash changes)
|
|
203
|
+
- Branch might not exist
|
|
204
|
+
|
|
205
|
+
**"Merge failed"**
|
|
206
|
+
- Might have conflicts to resolve
|
|
207
|
+
- Ensure both branches have commits
|
|
208
|
+
|
|
209
|
+
**"Push failed"**
|
|
210
|
+
- Check network connection
|
|
211
|
+
- Ensure you have permissions on remote
|
|
212
|
+
- May need to pull first
|
|
213
|
+
|
|
214
|
+
**"No changes to stage"**
|
|
215
|
+
- Working directory is clean
|
|
216
|
+
- All changes are already staged
|
|
217
|
+
|
|
218
|
+
## Next Steps
|
|
219
|
+
|
|
220
|
+
- Explore stash for temporary work
|
|
221
|
+
- Use branches for different features
|
|
222
|
+
- Tag releases for easy reference
|
|
223
|
+
- Push/Pull regularly to stay in sync
|
package/README.md
CHANGED
package/bin/index.js
CHANGED
|
@@ -1,50 +1,400 @@
|
|
|
1
|
-
#! /usr/bin/env node
|
|
2
|
-
import { dirname, join } from 'node:path';
|
|
3
|
-
import { fileURLToPath } from 'node:url';
|
|
4
|
-
import fastify from 'fastify';
|
|
5
|
-
import fastifyStatic from '@fastify/static';
|
|
6
|
-
import fastifyCors from '@fastify/cors';
|
|
7
|
-
import open from 'open';
|
|
8
|
-
import simpleGit from 'simple-git';
|
|
9
|
-
void (async function main() {
|
|
10
|
-
// Calculate dirname
|
|
11
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
12
|
-
// Get port number
|
|
13
|
-
const portArgIndex = process.argv.findIndex((a) => a.toLowerCase() === '--port');
|
|
14
|
-
const port = portArgIndex !== -1 ? +process.argv[portArgIndex + 1] : 8080;
|
|
15
|
-
// Get if should not open browser
|
|
16
|
-
const shouldOpenBrowser = process.argv.findIndex((a) => a.toLowerCase() === '--no-open') === -1;
|
|
17
|
-
// Initialize server
|
|
18
|
-
const app = fastify({
|
|
19
|
-
logger: false,
|
|
20
|
-
});
|
|
21
|
-
// Register cors
|
|
22
|
-
await app.register(fastifyCors);
|
|
23
|
-
// Register static file provider
|
|
24
|
-
app.register(fastifyStatic, {
|
|
25
|
-
root: join(__dirname, 'ui'),
|
|
26
|
-
});
|
|
27
|
-
// Serve UI
|
|
28
|
-
app.get('/', (_req, resp) => resp.sendFile('index.html'));
|
|
29
|
-
// Initialize git lib
|
|
30
|
-
const git = simpleGit(process.cwd());
|
|
31
|
-
//
|
|
32
|
-
app.get('/api/commits', (_req, resp) => {
|
|
33
|
-
git.log({}, (err, data) => {
|
|
34
|
-
resp.type('application/json').send(data.all);
|
|
35
|
-
});
|
|
36
|
-
});
|
|
37
|
-
app.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
1
|
+
#! /usr/bin/env node
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import fastify from 'fastify';
|
|
5
|
+
import fastifyStatic from '@fastify/static';
|
|
6
|
+
import fastifyCors from '@fastify/cors';
|
|
7
|
+
import open from 'open';
|
|
8
|
+
import simpleGit from 'simple-git';
|
|
9
|
+
void (async function main() {
|
|
10
|
+
// Calculate dirname
|
|
11
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
// Get port number
|
|
13
|
+
const portArgIndex = process.argv.findIndex((a) => a.toLowerCase() === '--port');
|
|
14
|
+
const port = portArgIndex !== -1 ? +process.argv[portArgIndex + 1] : 8080;
|
|
15
|
+
// Get if should not open browser
|
|
16
|
+
const shouldOpenBrowser = process.argv.findIndex((a) => a.toLowerCase() === '--no-open') === -1;
|
|
17
|
+
// Initialize server
|
|
18
|
+
const app = fastify({
|
|
19
|
+
logger: false,
|
|
20
|
+
});
|
|
21
|
+
// Register cors
|
|
22
|
+
await app.register(fastifyCors);
|
|
23
|
+
// Register static file provider
|
|
24
|
+
app.register(fastifyStatic, {
|
|
25
|
+
root: join(__dirname, 'ui'),
|
|
26
|
+
});
|
|
27
|
+
// Serve UI
|
|
28
|
+
app.get('/', (_req, resp) => resp.sendFile('index.html'));
|
|
29
|
+
// Initialize git lib
|
|
30
|
+
const git = simpleGit(process.cwd());
|
|
31
|
+
// ==================== Commits ====================
|
|
32
|
+
app.get('/api/commits', (_req, resp) => {
|
|
33
|
+
git.log({}, (err, data) => {
|
|
34
|
+
resp.type('application/json').send(data.all);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
app.post('/api/commit', async (req, resp) => {
|
|
38
|
+
try {
|
|
39
|
+
const { message, description } = req.body;
|
|
40
|
+
await git.commit([message, ...(description ? [description] : [])], { '--allow-empty': null });
|
|
41
|
+
resp.type('application/json').send({ success: true });
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
resp.status(400).type('application/json').send({ error: err.message });
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
app.post('/api/amend', async (req, resp) => {
|
|
48
|
+
try {
|
|
49
|
+
const { message, description } = req.body;
|
|
50
|
+
await git.commit([message, ...(description ? [description] : [])], {
|
|
51
|
+
'--amend': null,
|
|
52
|
+
'--no-edit': null,
|
|
53
|
+
});
|
|
54
|
+
resp.type('application/json').send({ success: true });
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
resp.status(400).type('application/json').send({ error: err.message });
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
app.post('/api/revert', async (req, resp) => {
|
|
61
|
+
try {
|
|
62
|
+
const { commit } = req.body;
|
|
63
|
+
await git.revert(commit);
|
|
64
|
+
resp.type('application/json').send({ success: true });
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
resp.status(400).type('application/json').send({ error: err.message });
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
// ==================== Staging ====================
|
|
71
|
+
app.get('/api/status', async (_req, resp) => {
|
|
72
|
+
try {
|
|
73
|
+
const status = await git.status();
|
|
74
|
+
resp.type('application/json').send(status);
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
resp.status(400).type('application/json').send({ error: err.message });
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
app.post('/api/stage', async (req, resp) => {
|
|
81
|
+
try {
|
|
82
|
+
const { files } = req.body;
|
|
83
|
+
if (Array.isArray(files) && files.length > 0) {
|
|
84
|
+
await git.add(files);
|
|
85
|
+
}
|
|
86
|
+
resp.type('application/json').send({ success: true });
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
resp.status(400).type('application/json').send({ error: err.message });
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
app.post('/api/unstage', async (req, resp) => {
|
|
93
|
+
try {
|
|
94
|
+
const { files } = req.body;
|
|
95
|
+
if (Array.isArray(files) && files.length > 0) {
|
|
96
|
+
await git.reset(['HEAD', ...files]);
|
|
97
|
+
}
|
|
98
|
+
resp.type('application/json').send({ success: true });
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
resp.status(400).type('application/json').send({ error: err.message });
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
app.post('/api/stage-lines', async (req, resp) => {
|
|
105
|
+
try {
|
|
106
|
+
const { file, patch } = req.body;
|
|
107
|
+
// Stage specific lines using git add -p equivalent (patch mode)
|
|
108
|
+
await git.raw('add --patch', { input: patch });
|
|
109
|
+
resp.type('application/json').send({ success: true });
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
resp.status(400).type('application/json').send({ error: err.message });
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
// ==================== Branches ====================
|
|
116
|
+
app.get('/api/branches', async (_req, resp) => {
|
|
117
|
+
try {
|
|
118
|
+
const branchSummary = await git.branchLocal();
|
|
119
|
+
resp.type('application/json').send(branchSummary);
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
resp.status(400).type('application/json').send({ error: err.message });
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
app.post('/api/branch/create', async (req, resp) => {
|
|
126
|
+
try {
|
|
127
|
+
const { name, startPoint } = req.body;
|
|
128
|
+
await git.branch([...(startPoint ? [name, startPoint] : [name])]);
|
|
129
|
+
resp.type('application/json').send({ success: true });
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
resp.status(400).type('application/json').send({ error: err.message });
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
app.post('/api/branch/delete', async (req, resp) => {
|
|
136
|
+
try {
|
|
137
|
+
const { name, force } = req.body;
|
|
138
|
+
await git.deleteLocalBranch(name, force);
|
|
139
|
+
resp.type('application/json').send({ success: true });
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
resp.status(400).type('application/json').send({ error: err.message });
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
app.post('/api/branch/rename', async (req, resp) => {
|
|
146
|
+
try {
|
|
147
|
+
const { oldName, newName } = req.body;
|
|
148
|
+
await git.branch(['-m', oldName, newName]);
|
|
149
|
+
resp.type('application/json').send({ success: true });
|
|
150
|
+
}
|
|
151
|
+
catch (err) {
|
|
152
|
+
resp.status(400).type('application/json').send({ error: err.message });
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
app.post('/api/checkout', async (req, resp) => {
|
|
156
|
+
try {
|
|
157
|
+
const { ref } = req.body;
|
|
158
|
+
await git.checkout(ref);
|
|
159
|
+
resp.type('application/json').send({ success: true });
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
resp.status(400).type('application/json').send({ error: err.message });
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
// ==================== Merge & Rebase ====================
|
|
166
|
+
app.post('/api/merge', async (req, resp) => {
|
|
167
|
+
try {
|
|
168
|
+
const { branch } = req.body;
|
|
169
|
+
await git.merge([branch]);
|
|
170
|
+
resp.type('application/json').send({ success: true });
|
|
171
|
+
}
|
|
172
|
+
catch (err) {
|
|
173
|
+
resp.status(400).type('application/json').send({ error: err.message });
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
app.post('/api/rebase', async (req, resp) => {
|
|
177
|
+
try {
|
|
178
|
+
const { branch } = req.body;
|
|
179
|
+
await git.rebase([branch]);
|
|
180
|
+
resp.type('application/json').send({ success: true });
|
|
181
|
+
}
|
|
182
|
+
catch (err) {
|
|
183
|
+
resp.status(400).type('application/json').send({ error: err.message });
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
app.post('/api/squash', async (req, resp) => {
|
|
187
|
+
try {
|
|
188
|
+
const { commits } = req.body;
|
|
189
|
+
// Squash: rebase -i with squash operation
|
|
190
|
+
await git.rebase(['-i', 'HEAD~' + commits]);
|
|
191
|
+
resp.type('application/json').send({ success: true });
|
|
192
|
+
}
|
|
193
|
+
catch (err) {
|
|
194
|
+
resp.status(400).type('application/json').send({ error: err.message });
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
// ==================== Stash ====================
|
|
198
|
+
app.get('/api/stash/list', async (_req, resp) => {
|
|
199
|
+
try {
|
|
200
|
+
const stashList = await git.stashList();
|
|
201
|
+
resp.type('application/json').send(stashList);
|
|
202
|
+
}
|
|
203
|
+
catch (err) {
|
|
204
|
+
resp.status(400).type('application/json').send({ error: err.message });
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
app.post('/api/stash/save', async (req, resp) => {
|
|
208
|
+
try {
|
|
209
|
+
const { message } = req.body;
|
|
210
|
+
await git.stash(['save', message || '']);
|
|
211
|
+
resp.type('application/json').send({ success: true });
|
|
212
|
+
}
|
|
213
|
+
catch (err) {
|
|
214
|
+
resp.status(400).type('application/json').send({ error: err.message });
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
app.post('/api/stash/apply', async (req, resp) => {
|
|
218
|
+
try {
|
|
219
|
+
const { index } = req.body;
|
|
220
|
+
await git.stash(['apply', `stash@{${index}}`]);
|
|
221
|
+
resp.type('application/json').send({ success: true });
|
|
222
|
+
}
|
|
223
|
+
catch (err) {
|
|
224
|
+
resp.status(400).type('application/json').send({ error: err.message });
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
app.post('/api/stash/pop', async (req, resp) => {
|
|
228
|
+
try {
|
|
229
|
+
const { index } = req.body;
|
|
230
|
+
await git.stash(['pop', `stash@{${index}}`]);
|
|
231
|
+
resp.type('application/json').send({ success: true });
|
|
232
|
+
}
|
|
233
|
+
catch (err) {
|
|
234
|
+
resp.status(400).type('application/json').send({ error: err.message });
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
app.post('/api/stash/drop', async (req, resp) => {
|
|
238
|
+
try {
|
|
239
|
+
const { index } = req.body;
|
|
240
|
+
await git.stash(['drop', `stash@{${index}}`]);
|
|
241
|
+
resp.type('application/json').send({ success: true });
|
|
242
|
+
}
|
|
243
|
+
catch (err) {
|
|
244
|
+
resp.status(400).type('application/json').send({ error: err.message });
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
app.post('/api/stash/show', async (req, resp) => {
|
|
248
|
+
try {
|
|
249
|
+
const { index } = req.body;
|
|
250
|
+
const show = await git.stash(['show', '-p', `stash@{${index}}`]);
|
|
251
|
+
resp.type('application/json').send({ preview: show });
|
|
252
|
+
}
|
|
253
|
+
catch (err) {
|
|
254
|
+
resp.status(400).type('application/json').send({ error: err.message });
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
// ==================== Tags ====================
|
|
258
|
+
app.get('/api/tags', async (_req, resp) => {
|
|
259
|
+
try {
|
|
260
|
+
const tags = await git.tags();
|
|
261
|
+
resp.type('application/json').send(tags);
|
|
262
|
+
}
|
|
263
|
+
catch (err) {
|
|
264
|
+
resp.status(400).type('application/json').send({ error: err.message });
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
app.post('/api/tag/create', async (req, resp) => {
|
|
268
|
+
try {
|
|
269
|
+
const { name, message, commit } = req.body;
|
|
270
|
+
if (message) {
|
|
271
|
+
await git.tag(['-a', name, '-m', message, ...(commit ? [commit] : [])]);
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
await git.tag([name, ...(commit ? [commit] : [])]);
|
|
275
|
+
}
|
|
276
|
+
resp.type('application/json').send({ success: true });
|
|
277
|
+
}
|
|
278
|
+
catch (err) {
|
|
279
|
+
resp.status(400).type('application/json').send({ error: err.message });
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
app.post('/api/tag/delete', async (req, resp) => {
|
|
283
|
+
try {
|
|
284
|
+
const { name } = req.body;
|
|
285
|
+
await git.tag(['-d', name]);
|
|
286
|
+
resp.type('application/json').send({ success: true });
|
|
287
|
+
}
|
|
288
|
+
catch (err) {
|
|
289
|
+
resp.status(400).type('application/json').send({ error: err.message });
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
// ==================== Remote Operations ====================
|
|
293
|
+
app.get('/api/fetch', (_req, resp) => {
|
|
294
|
+
git.fetch({}, (err, data) => {
|
|
295
|
+
resp.type('application/json').send();
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
app.post('/api/pull', async (req, resp) => {
|
|
299
|
+
try {
|
|
300
|
+
const { remote, branch } = req.body;
|
|
301
|
+
await git.pull(remote || 'origin', branch || undefined);
|
|
302
|
+
resp.type('application/json').send({ success: true });
|
|
303
|
+
}
|
|
304
|
+
catch (err) {
|
|
305
|
+
resp.status(400).type('application/json').send({ error: err.message });
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
app.post('/api/push', async (req, resp) => {
|
|
309
|
+
try {
|
|
310
|
+
const { remote, branch, force } = req.body;
|
|
311
|
+
await git.push(remote || 'origin', branch || undefined, force ? { '-f': null } : {});
|
|
312
|
+
resp.type('application/json').send({ success: true });
|
|
313
|
+
}
|
|
314
|
+
catch (err) {
|
|
315
|
+
resp.status(400).type('application/json').send({ error: err.message });
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
app.post('/api/prune', async (req, resp) => {
|
|
319
|
+
try {
|
|
320
|
+
const { remote } = req.body;
|
|
321
|
+
await git.remote(['prune', remote || 'origin']);
|
|
322
|
+
resp.type('application/json').send({ success: true });
|
|
323
|
+
}
|
|
324
|
+
catch (err) {
|
|
325
|
+
resp.status(400).type('application/json').send({ error: err.message });
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
// ==================== Diff Viewer ====================
|
|
329
|
+
app.post('/api/commit/diff', async (req, resp) => {
|
|
330
|
+
try {
|
|
331
|
+
const { hash } = req.body;
|
|
332
|
+
if (!hash) {
|
|
333
|
+
return resp.status(400).type('application/json').send({ error: 'hash required' });
|
|
334
|
+
}
|
|
335
|
+
// Get raw diff using git show
|
|
336
|
+
const rawDiff = await git.show([hash]);
|
|
337
|
+
// Split by file sections (diff --git lines)
|
|
338
|
+
const files = [];
|
|
339
|
+
const sections = rawDiff.split(/(?=^diff --git)/m);
|
|
340
|
+
for (const section of sections) {
|
|
341
|
+
if (!section.trim())
|
|
342
|
+
continue;
|
|
343
|
+
// Extract filename from "diff --git a/path b/path"
|
|
344
|
+
const fileMatch = section.match(/^diff --git a\/(.*?) b\/(.*)$/m);
|
|
345
|
+
if (!fileMatch)
|
|
346
|
+
continue;
|
|
347
|
+
const filepath = fileMatch[2];
|
|
348
|
+
// Extract the actual diff content
|
|
349
|
+
const contentMatch = section.match(/^@@ .+? @@\s*\n([\s\S]*?)(?=^diff --git|$)/m);
|
|
350
|
+
if (!contentMatch) {
|
|
351
|
+
// File may be binary or new
|
|
352
|
+
files.push({
|
|
353
|
+
path: filepath,
|
|
354
|
+
original: '',
|
|
355
|
+
modified: '',
|
|
356
|
+
isBinary: section.includes('Binary files'),
|
|
357
|
+
});
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
const diffContent = contentMatch[1];
|
|
361
|
+
let original = '';
|
|
362
|
+
let modified = '';
|
|
363
|
+
// Parse unified diff format
|
|
364
|
+
const diffLines = diffContent.split('\n');
|
|
365
|
+
for (const line of diffLines) {
|
|
366
|
+
if (line.startsWith('-') && !line.startsWith('---')) {
|
|
367
|
+
original += line.substring(1) + '\n';
|
|
368
|
+
}
|
|
369
|
+
else if (line.startsWith('+') && !line.startsWith('+++')) {
|
|
370
|
+
modified += line.substring(1) + '\n';
|
|
371
|
+
}
|
|
372
|
+
else if (!line.startsWith('\\')) {
|
|
373
|
+
// Context lines appear in both
|
|
374
|
+
const contextLine = line.substring(1) + '\n';
|
|
375
|
+
original += contextLine;
|
|
376
|
+
modified += contextLine;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
files.push({
|
|
380
|
+
path: filepath,
|
|
381
|
+
original: original.trimEnd(),
|
|
382
|
+
modified: modified.trimEnd(),
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
resp.type('application/json').send({ hash, files });
|
|
386
|
+
}
|
|
387
|
+
catch (err) {
|
|
388
|
+
console.error('Diff error:', err.message);
|
|
389
|
+
resp.status(400).type('application/json').send({ error: err.message });
|
|
390
|
+
}
|
|
391
|
+
});
|
|
392
|
+
// Run the server
|
|
393
|
+
app.listen({ port }, (err, address) => {
|
|
394
|
+
if (err)
|
|
395
|
+
throw err;
|
|
396
|
+
console.log(`Visit "${address}" to see git explorer.`);
|
|
397
|
+
if (shouldOpenBrowser)
|
|
398
|
+
open(address);
|
|
399
|
+
});
|
|
400
|
+
})();
|