@nemo-cli/git 0.1.3 → 0.1.4

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 CHANGED
@@ -19,6 +19,8 @@ pnpm add @nemo-cli/git --global
19
19
  - **Conventional Commits**: Interactive commit wizard with type/scope selection from your commitlint config
20
20
  - **Smart Branch Management**: Advanced branch operations with merge status and time filtering
21
21
  - **Ticket Auto-Detection**: Automatically extracts ticket numbers from branch names for commit messages
22
+ - **Interactive Commit Navigator**: Enhanced `ng blame` for browsing file history with full diff support
23
+ - **Visual History Viewer**: Beautiful `ng hist` command with interactive graph display and keyboard navigation
22
24
 
23
25
  ## Usage
24
26
 
@@ -35,6 +37,8 @@ ng <command> -h
35
37
  # Example:
36
38
  ng commit -h
37
39
  ng branch -h
40
+ ng blame -h
41
+ ng hist -h
38
42
  ```
39
43
 
40
44
  ---
@@ -239,6 +243,180 @@ ng merge -r
239
243
 
240
244
  ---
241
245
 
246
+ ### Interactive Commit Navigator (`ng blame`)
247
+
248
+ Browse file commit history with full diff support and interactive navigation.
249
+
250
+ ```bash
251
+ # View commit history for a file
252
+ ng blame <file-path>
253
+
254
+ # Example
255
+ ng blame src/commands/blame.ts
256
+ ng blame packages/git/src/commands/blame.ts
257
+ ```
258
+
259
+ **Features:**
260
+ - ✅ **Full Diff Display**: Shows complete diff for each commit (not just commit messages)
261
+ - ✅ **Interactive Navigation**: Browse through commits with keyboard shortcuts
262
+ - ✅ **Smart Caching**: Fetches git history once, then navigates instantly
263
+ - ✅ **Binary File Support**: Detects and handles binary files gracefully
264
+ - ✅ **Large Diff Protection**: Limits display to 50 lines to prevent terminal overflow
265
+ - ✅ **Follow File Renames**: Uses `--follow` to track history across renames
266
+
267
+ **Interactive Controls:**
268
+
269
+ | Key | Action | Description |
270
+ |-----|--------|-------------|
271
+ | `n` | Next commit | Move forward in time (to newer commits) |
272
+ | `p` | Previous commit | Move backward in time (to older commits) |
273
+ | `j` | Jump | Jump to a specific commit by number |
274
+ | `q` | Quit | Exit the navigator |
275
+
276
+ **Display Information:**
277
+
278
+ Each commit shows:
279
+ - 📝 Commit number (e.g., `Commit 3/10`)
280
+ - 🔖 Short commit hash (8 characters, colored)
281
+ - 👤 Author name (colored)
282
+ - 📅 Commit date (dimmed)
283
+ - 💬 Commit message
284
+ - 📄 Full diff with git standard formatting (red for deletions, green for additions)
285
+
286
+ **Special Handling:**
287
+
288
+ - **Binary Files**: Shows "📄 Binary file - diff not available" instead of binary content
289
+ - **Large Diffs**: Displays first 50 lines with truncation notice
290
+ ```
291
+ (Showing first 50 lines of 123)
292
+ ... (truncated)
293
+ ```
294
+ - **Empty History**: Warns if file has no git history
295
+ - **Missing Files**: Clear error if file doesn't exist
296
+
297
+ **Example Output:**
298
+
299
+ ```
300
+ Found 10 commits for src/commands/blame.ts
301
+ Use [n/p] to navigate, [j] to jump, [q] to quit
302
+
303
+ 📝 Commit 1/10
304
+ abc123de - John Doe - Mon Feb 2 12:00:00 2026
305
+ feat(git): add interactive commit navigator
306
+
307
+ --- Diff ---
308
+ diff --git a/src/commands/blame.ts b/src/commands/blame.ts
309
+ new file mode 100644
310
+ index 0000000..1234567
311
+ --- /dev/null
312
+ +++ b/src/commands/blame.ts
313
+ @@ -0,0 +1,315 @@
314
+ +import path from 'node:path'
315
+ +import readline from 'node:readline'
316
+ ...
317
+
318
+ --- Actions ---
319
+ [n] Next commit [p] Previous commit [j] Jump [q] Quit
320
+ ```
321
+
322
+ **Use Cases:**
323
+
324
+ - 📖 **Code Review**: Understand how a file evolved over time
325
+ - 🐛 **Bug Investigation**: Find when a specific line was changed
326
+ - 📚 **Learning**: Study the development history of a feature
327
+ - 🔍 **Audit**: Review all changes made to a critical file
328
+
329
+ ---
330
+
331
+ ### Git History Viewer (`ng hist` / `ng history`)
332
+
333
+ Display git history with an interactive, scrollable graph view.
334
+
335
+ ```bash
336
+ # Show full git history
337
+ ng hist
338
+
339
+ # Limit number of commits
340
+ ng hist -n 20
341
+ ng hist --number 50
342
+
343
+ # Using alias
344
+ ng history
345
+ ng history -n 10
346
+ ```
347
+
348
+ **Features:**
349
+ - ✅ **Beautiful Graph Format**: Visualizes branch structure with commit tree
350
+ - ✅ **Interactive Navigation**: Scroll through history with keyboard or mouse
351
+ - ✅ **Optimized Display**: Automatically adjusts to terminal size
352
+ - ✅ **Status Bar**: Shows current position and available shortcuts
353
+ - ✅ **Color-Coded Output**:
354
+ - Cyan: Commit hash
355
+ - Green: Commit date
356
+ - Magenta: Author name
357
+ - Yellow: Branch references
358
+
359
+ **Interactive Controls:**
360
+
361
+ | Key | Action | Description |
362
+ |-----|--------|-------------|
363
+ | `↑` / `k` | Scroll up | Move up through commits |
364
+ | `↓` / `j` | Scroll down | Move down through commits |
365
+ | `gg` | Jump to top | Go to the oldest commit |
366
+ | `G` | Jump to bottom | Go to the newest commit (Shift+G) |
367
+ | `Page Up` | Page up | Scroll up one page |
368
+ | `Page Down` | Page down | Scroll down one page |
369
+ | `q` / `Enter` | Quit | Exit the viewer |
370
+
371
+ **Display Information:**
372
+
373
+ Each commit shows:
374
+ - 🔖 Short commit hash (cyan, bold)
375
+ - 📅 Commit date and time (green)
376
+ - 👤 Author name (magenta)
377
+ - 🌿 Branch and tag references (yellow)
378
+ - 💬 Commit message
379
+
380
+ **Status Bar:**
381
+
382
+ ```
383
+ ↑↓/jk: Scroll | gg/G: Top/Bottom | PgUp/PgDn | q: Quit | Lines 1-42/150
384
+ ```
385
+
386
+ **Layout Optimization:**
387
+
388
+ - Automatically calculates optimal view height based on terminal size
389
+ - Reserves space for UI elements (borders, status bar)
390
+ - Ensures minimum of 10 lines for content display
391
+ - Removes unnecessary margins for maximum content visibility
392
+
393
+ **Use Cases:**
394
+
395
+ - 📊 **Project Overview**: Quickly see commit history and branch structure
396
+ - 🔍 **Context Browsing**: Understand recent changes before switching branches
397
+ - 📝 **Review History**: Check recent commits before pulling or pushing
398
+ - 🎯 **Navigation**: Find specific commits in the history
399
+
400
+ **Example Output:**
401
+
402
+ ```
403
+ ┌─────────────────────────────────────────────────────────────────┐
404
+ │* abc123de 2026-02-06 14:54:23 [GaoZimeng] (HEAD -> main) │
405
+ ││ refactor(git): main increase hist viewer height line │
406
+ │* 1a40997 2026-02-06 14:52:15 [GaoZimeng] │
407
+ ││ feat(git): fetch remote branches before pull │
408
+ │* a3be508 2026-02-06 14:51:23 [GaoZimeng] │
409
+ ││ refactor(git): change branch selection from search to select│
410
+ │* 172403f 2026-02-06 14:50:12 [GaoZimeng] │
411
+ ││ feat(git): enhance merge command with commit customization │
412
+ ├─────────────────────────────────────────────────────────────────┤
413
+ │ ↑↓/jk: Scroll | gg/G: Top/Bottom | PgUp/PgDn | q: Quit | Lines │
414
+ │ 1-10/150 │
415
+ └─────────────────────────────────────────────────────────────────┘
416
+ ```
417
+
418
+ ---
419
+
242
420
  ### Stash Operations (`ng stash` / `ng st`)
243
421
 
244
422
  Advanced stash management.
@@ -418,6 +596,93 @@ ng branch clean
418
596
  # Confirm deletion
419
597
  ```
420
598
 
599
+ ### Investigating File History
600
+
601
+ ```bash
602
+ # Understand how a file evolved over time
603
+ ng blame src/utils/date.ts
604
+
605
+ # Interactive navigation:
606
+ # - Press 'n' to see next commit
607
+ # - Press 'p' to go back to previous commit
608
+ # - Press 'j' to jump to commit 5/10
609
+ # - Press 'q' when done reviewing
610
+
611
+ # Each commit shows:
612
+ # - Full commit hash, author, date, message
613
+ # - Complete diff (what changed)
614
+ # - Current position (e.g., "Commit 3/10")
615
+ ```
616
+
617
+ **Real-world scenarios:**
618
+
619
+ - 🐛 **Bug Investigation**: Find when a bug was introduced
620
+ ```bash
621
+ ng blame src/auth/login.ts
622
+ # Press 'n' repeatedly to review changes chronologically
623
+ # Look for the commit that broke the functionality
624
+ ```
625
+
626
+ - 📖 **Code Review**: Understand the evolution of a complex function
627
+ ```bash
628
+ ng blame src/api/handlers.ts
629
+ # Navigate through commits to see how the logic developed
630
+ ```
631
+
632
+ - 🔍 **Audit Trail**: Review all changes to a security-critical file
633
+ ```bash
634
+ ng blame src/config/security.ts
635
+ # Use 'j' to jump to specific commits of interest
636
+ ```
637
+
638
+ ---
639
+
640
+ ### Browsing Project History
641
+
642
+ ```bash
643
+ # View full git history with beautiful graph
644
+ ng hist
645
+
646
+ # View last 20 commits
647
+ ng hist -n 20
648
+
649
+ # Interactive navigation:
650
+ # - Use ↑/↓ or j/k to scroll through commits
651
+ # - Press 'gg' to jump to oldest commit
652
+ # - Press 'G' (Shift+G) to jump to newest commit
653
+ # - Use Page Up/Down to scroll by pages
654
+ # - Press 'q' or Enter to exit
655
+
656
+ # Features:
657
+ # - Color-coded output (hash, date, author, branches)
658
+ # - Visual commit graph showing branch structure
659
+ # - Status bar showing current position
660
+ # - Automatically adjusts to terminal size
661
+ ```
662
+
663
+ **Real-world scenarios:**
664
+
665
+ - 📊 **Before Pulling**: Check what's been committed recently
666
+ ```bash
667
+ ng hist -n 10
668
+ # Review recent commits before doing `ng pull`
669
+ ```
670
+
671
+ - 🎯 **Finding Commits**: Locate a specific commit in history
672
+ ```bash
673
+ ng hist
674
+ # Press 'gg' to go to oldest commit
675
+ # Use ↓/j to scroll forward to find what you need
676
+ # Note the commit hash (e.g., abc123de)
677
+ ```
678
+
679
+ - 🌿 **Branch Overview**: Understand branch structure and merges
680
+ ```bash
681
+ ng hist -n 50
682
+ # See how branches diverged and merged
683
+ # Identify branch points and merge commits
684
+ ```
685
+
421
686
  ---
422
687
 
423
688
  ## Comparison: Git vs `ng`
@@ -432,6 +697,18 @@ ng branch clean
432
697
  | List branches | `git branch` | `ng list` (enhanced display) |
433
698
  | Merge | `git merge` | `ng merge` (auto stash + searchable) |
434
699
  | Stash | `git stash` | `ng stash` (enhanced management) |
700
+ | Blame | `git blame` (line-by-line) | `ng blame` (full commit history with diff) |
701
+ | Log/History | `git log` (static output) | `ng hist` (interactive graph viewer) |
702
+
703
+ **Key Difference - `ng blame` vs `git blame`:**
704
+
705
+ | Feature | `git blame` | `ng blame` |
706
+ |---------|-------------|------------|
707
+ | Shows | Line-by-line annotations | Full commit history with diffs |
708
+ | Navigation | Scroll through file | Interactive commit navigation (n/p/j/q) |
709
+ | Diff View | No (use separately) | Yes, included for each commit |
710
+ | File Renames | Limited | Full support with `--follow` |
711
+ | Best For | Finding who changed a line | Understanding file evolution |
435
712
 
436
713
  ---
437
714
 
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { addFiles, colors, createCheckbox, createCommand, createConfirm, createHelpExample, createInput, createNote, createOptions, createSearch, createSelect, createSpinner, exit, getCurrentBranch, getGitStatus, intro, isEmpty, isString, loadConfig, log, outro, readPackage, x, xASync } from "@nemo-cli/shared";
1
+ import { addFiles, colors, createCheckbox, createCommand, createConfirm, createHelpExample, createInput, createNote, createOptions, createSelect, createSpinner, exit, getCurrentBranch, getGitStatus, intro, isEmpty, isString, loadConfig, log, outro, readPackage, safeAwait, x, xASync } from "@nemo-cli/shared";
2
2
  import { BigText, ErrorMessage, Message, renderHistViewer, renderStashList, renderStatusViewer } from "@nemo-cli/ui";
3
3
  import path, { dirname, join } from "node:path";
4
4
  import readline from "node:readline";
@@ -376,7 +376,7 @@ const getLocalBranches = async () => {
376
376
  currentBranch: formatBranch$1(currentBranch)
377
377
  };
378
378
  };
379
- const getRemoteOptions = async () => {
379
+ const getRemoteBranchOptions = async () => {
380
380
  const { branches } = await getRemoteBranches();
381
381
  const currentBranch = await getCurrentBranch();
382
382
  return {
@@ -388,7 +388,7 @@ const getRemoteOptions = async () => {
388
388
  currentBranch
389
389
  };
390
390
  };
391
- const getLocalOptions = async () => {
391
+ const getLocalBranchOptions = async () => {
392
392
  const { branches, currentBranch } = await getLocalBranches();
393
393
  return {
394
394
  options: branches.map((branch) => ({
@@ -399,7 +399,7 @@ const getLocalOptions = async () => {
399
399
  currentBranch
400
400
  };
401
401
  };
402
- const getRemoteOptionsForRemotes = async () => {
402
+ const getRemoteRepositoryOptions = async () => {
403
403
  const { remotes } = await getRemotes();
404
404
  const validRemotes = remotes.filter((remote) => remote.trim().length > 0);
405
405
  const uniqueRemotes = Array.from(new Set(validRemotes));
@@ -494,18 +494,18 @@ const handleMergeCommit = async () => {
494
494
  }
495
495
  };
496
496
  const handleGitPull = async (branch, options = {}) => {
497
- const { rebase = false } = options;
497
+ const { rebase = false, remote = "origin" } = options;
498
498
  const modeText = rebase ? "rebase" : "merge";
499
499
  log.show(`Pulling from remote (${modeText} mode)...`, { type: "step" });
500
500
  try {
501
501
  const [error, result] = await xASync("git", rebase ? [
502
502
  "pull",
503
503
  "--rebase",
504
- "origin",
504
+ remote,
505
505
  branch
506
506
  ] : [
507
507
  "pull",
508
- "origin",
508
+ remote,
509
509
  branch
510
510
  ], { nodeOptions: { stdio: "inherit" } });
511
511
  if (error) {
@@ -881,13 +881,13 @@ function checkoutCommand(command) {
881
881
  initialValue: true
882
882
  });
883
883
  if (isLocal) {
884
- const { options } = await getLocalOptions();
884
+ const { options } = await getLocalBranchOptions();
885
885
  handleCheckout(await createSelect({
886
886
  message: `Select the ${colors.bgGreen(" local ")} branch to checkout`,
887
887
  options
888
888
  }));
889
889
  } else {
890
- const { options } = await getRemoteOptions();
890
+ const { options } = await getRemoteBranchOptions();
891
891
  const selectedBranch = await createSelect({
892
892
  message: `Select the ${colors.bgYellow(" remote ")} branch to checkout`,
893
893
  options
@@ -1089,50 +1089,50 @@ const pushInteractive = async () => {
1089
1089
  log.error("No branch selected. Aborting push operation.");
1090
1090
  return;
1091
1091
  }
1092
- let remotes;
1092
+ let repositories;
1093
1093
  try {
1094
- remotes = (await getRemoteOptionsForRemotes()).remotes;
1094
+ repositories = (await getRemoteRepositoryOptions()).remotes;
1095
1095
  } catch (error) {
1096
1096
  log.error(`Failed to get remote repositories: ${error instanceof Error ? error.message : String(error)}`);
1097
1097
  log.show("Hint: Make sure you're in a git repository and have configured remotes.", { type: "info" });
1098
1098
  return;
1099
1099
  }
1100
- if (remotes.length === 0) {
1100
+ if (repositories.length === 0) {
1101
1101
  log.error("No remote repositories found. Aborting push operation.");
1102
1102
  log.show("Hint: Run `git remote add <name> <url>` to add a remote repository.", { type: "info" });
1103
1103
  return;
1104
1104
  }
1105
- let selectedRemote = remotes[0];
1106
- if (remotes.length > 1) selectedRemote = await createSelect({
1105
+ let selectedRepository = repositories[0];
1106
+ if (repositories.length > 1) selectedRepository = await createSelect({
1107
1107
  message: "Select remote repository",
1108
- options: remotes.map((remote) => ({
1109
- label: remote,
1110
- value: remote
1108
+ options: repositories.map((repo) => ({
1109
+ label: repo,
1110
+ value: repo
1111
1111
  })),
1112
- initialValue: remotes[0]
1112
+ initialValue: repositories[0]
1113
1113
  });
1114
- if (await createConfirm({ message: `Do you want to push ${colors.bgGreen(currentBranch)} to ${selectedRemote}?` })) {
1115
- await handlePush(currentBranch, selectedRemote);
1114
+ if (await createConfirm({ message: `Do you want to push ${colors.bgGreen(currentBranch)} to ${selectedRepository}?` })) {
1115
+ await handlePush(currentBranch, selectedRepository);
1116
1116
  return;
1117
1117
  }
1118
- const { options } = await getRemoteOptions();
1118
+ const { options } = await getRemoteBranchOptions();
1119
1119
  const selectedBranch = await createSelect({
1120
1120
  message: "Select the branch to push",
1121
1121
  options,
1122
1122
  initialValue: "main"
1123
1123
  });
1124
- let pushRemote = selectedRemote;
1125
- if (remotes.length > 1) {
1126
- if (!await createConfirm({ message: `Push ${colors.bgGreen(selectedBranch)} to ${selectedRemote}?` })) pushRemote = await createSelect({
1124
+ selectedRepository = repositories[0];
1125
+ if (repositories.length > 1) {
1126
+ if (!await createConfirm({ message: `Push ${colors.bgGreen(selectedBranch)} to ${selectedRepository}?` })) selectedRepository = await createSelect({
1127
1127
  message: "Select remote repository",
1128
- options: remotes.map((remote) => ({
1129
- label: remote,
1130
- value: remote
1128
+ options: repositories.map((repo) => ({
1129
+ label: repo,
1130
+ value: repo
1131
1131
  })),
1132
- initialValue: selectedRemote
1132
+ initialValue: selectedRepository
1133
1133
  });
1134
1134
  }
1135
- await handlePush(selectedBranch, pushRemote);
1135
+ await handlePush(selectedBranch, selectedRepository);
1136
1136
  };
1137
1137
 
1138
1138
  //#endregion
@@ -1807,24 +1807,43 @@ function listCommand(command) {
1807
1807
  //#region src/commands/merge.ts
1808
1808
  const handleMerge = async (branch) => {
1809
1809
  const spinner = createSpinner(`Merging branch ${branch}...`);
1810
- const args = ["merge", branch];
1810
+ const args = [
1811
+ "merge",
1812
+ "--no-edit",
1813
+ branch
1814
+ ];
1811
1815
  const stashResult = await handleGitStash(void 0, {
1812
1816
  branch,
1813
1817
  operation: "merge"
1814
1818
  });
1819
+ if (!stashResult) {
1820
+ spinner.stop("Cannot proceed with merge: failed to stash changes.");
1821
+ return;
1822
+ }
1815
1823
  try {
1816
- const [error] = await xASync("git", args, { nodeOptions: { stdio: "inherit" } });
1817
- if (error) log.show(`Failed to merge branch ${branch}.`, { type: "error" });
1818
- else spinner.stop(`Successfully merged branch ${branch}.`);
1824
+ const [error, result] = await xASync("git", args, { nodeOptions: { stdio: "inherit" } });
1825
+ if (error) {
1826
+ spinner.stop();
1827
+ const errorMessage = error instanceof Error ? error.message : String(error);
1828
+ throw new Error(`Failed to merge branch ${branch}: ${errorMessage}`);
1829
+ }
1830
+ spinner.stop();
1831
+ if (result.stdout.includes("Merge branch") || result.stdout.includes("Merge made by")) await handleMergeCommit();
1832
+ log.show(colors.green(`Successfully merged branch ${colors.bgGreen(branch)}.`), { type: "success" });
1833
+ } catch (error) {
1834
+ spinner.stop();
1835
+ log.show(`Failed to merge branch ${branch}.`, { type: "error" });
1836
+ log.error(error);
1837
+ throw error;
1819
1838
  } finally {
1820
- stashResult && handleGitPop(stashResult);
1839
+ if (stashResult) await handleGitPop(stashResult);
1821
1840
  }
1822
1841
  };
1823
1842
  function mergeCommand(command) {
1824
1843
  command.command("merge").alias("mg").argument("[branch]", "The branch to merge").option("-l, --local", "Merge a local branch").option("-r, --remote", "Merge a remote branch").option("-b, --branch <branch>", "Create and merge a new branch").description("Merge a branch").action(async (branch, params) => {
1825
1844
  let isLocal = params.local;
1826
1845
  if (branch) {
1827
- handleMerge(branch);
1846
+ await handleMerge(branch);
1828
1847
  return;
1829
1848
  }
1830
1849
  if (isEmpty(params)) isLocal = await createSelect({
@@ -1839,18 +1858,20 @@ function mergeCommand(command) {
1839
1858
  initialValue: false
1840
1859
  });
1841
1860
  if (isLocal) {
1842
- const { options } = await getLocalOptions();
1843
- handleMerge(await createSearch({
1861
+ const { options, currentBranch } = await getLocalBranchOptions();
1862
+ await handleMerge(await createSelect({
1844
1863
  message: "Select the branch to merge",
1845
- options
1864
+ options,
1865
+ initialValue: currentBranch
1846
1866
  }));
1847
1867
  } else {
1848
- const { options } = await getRemoteOptions();
1849
- const selectedBranch = await createSearch({
1868
+ const { options, currentBranch } = await getRemoteBranchOptions();
1869
+ const selectedBranch = await createSelect({
1850
1870
  message: "Select the branch to merge",
1851
- options
1871
+ options,
1872
+ initialValue: currentBranch
1852
1873
  });
1853
- if (await createConfirm({ message: `Do you want to merge ${colors.bgRed(selectedBranch)}?` })) handleMerge(selectedBranch);
1874
+ if (await createConfirm({ message: `Do you want to merge ${colors.bgRed(selectedBranch)}?` })) await handleMerge(selectedBranch);
1854
1875
  }
1855
1876
  });
1856
1877
  }
@@ -1859,7 +1880,40 @@ function mergeCommand(command) {
1859
1880
  //#region src/commands/pull.ts
1860
1881
  function pullCommand(command) {
1861
1882
  command.command("pull").alias("pl").description("Pull git branch").option("-r, --rebase", "Use rebase mode instead of merge").option("-m, --merge", "Use merge mode (default)").action(async (options) => {
1862
- const { options: branchOptions, currentBranch } = await getRemoteOptions();
1883
+ const [error, result] = await safeAwait(getRemoteRepositoryOptions());
1884
+ if (error) {
1885
+ log.error(`Failed to get remote repositories: ${error.message}`);
1886
+ return;
1887
+ }
1888
+ const repositories = result.remotes;
1889
+ if (repositories.length === 0) {
1890
+ log.error("No remote repositories found. Aborting pull operation.");
1891
+ log.show("Hint: Use \"git remote add <name> <url>\" to add a remote repository.", { type: "info" });
1892
+ return;
1893
+ }
1894
+ const selectedRepository = repositories.length > 1 ? (await safeAwait(createSelect({
1895
+ message: "Select remote repository",
1896
+ options: result.options,
1897
+ initialValue: repositories[0]
1898
+ })))[1] : repositories[0];
1899
+ if (!selectedRepository) {
1900
+ log.error("No remote selected. Aborting pull operation.");
1901
+ return;
1902
+ }
1903
+ const spinner = createSpinner("Fetching latest remote branches...");
1904
+ const [fetchError] = await xASync("git", [
1905
+ "fetch",
1906
+ selectedRepository,
1907
+ "--prune"
1908
+ ], { timeout: 3e4 });
1909
+ if (fetchError) {
1910
+ spinner.stop("Failed to fetch latest remote branches");
1911
+ log.error(`Failed to fetch from ${selectedRepository}: ${fetchError.message}`);
1912
+ log.show("Please check your network connection and try again.", { type: "info" });
1913
+ return;
1914
+ }
1915
+ spinner.stop("Successfully fetched latest remote branches");
1916
+ const { options: branchOptions, currentBranch } = await getRemoteBranchOptions();
1863
1917
  if (!branchOptions.length) {
1864
1918
  log.error("No branches found. Please check your git repository.");
1865
1919
  return;
@@ -1879,11 +1933,11 @@ function pullCommand(command) {
1879
1933
  options: [{
1880
1934
  label: "Merge (default)",
1881
1935
  value: "merge",
1882
- hint: "git pull origin <branch>"
1936
+ hint: `git pull ${selectedRepository} ${selectedBranch}`
1883
1937
  }, {
1884
1938
  label: "Rebase",
1885
1939
  value: "rebase",
1886
- hint: "git pull --rebase origin <branch>"
1940
+ hint: `git pull --rebase ${selectedRepository} ${selectedBranch}`
1887
1941
  }],
1888
1942
  initialValue: "merge"
1889
1943
  }) === "rebase";
@@ -1891,7 +1945,10 @@ function pullCommand(command) {
1891
1945
  branch: selectedBranch,
1892
1946
  operation: "pull"
1893
1947
  });
1894
- await handleGitPull(selectedBranch, { rebase: useRebase });
1948
+ await handleGitPull(selectedBranch, {
1949
+ remote: selectedRepository,
1950
+ rebase: useRebase
1951
+ });
1895
1952
  stashResult && handleGitPop(stashResult);
1896
1953
  });
1897
1954
  }