fortrangoingonforty/fuss / 2315169

Browse files

c to bat/cat/(pager) y to cherry pick with fzf fdialog

Authored by espadonne
SHA
2315169dfeeb9d6feee0630196591a09b33f1bec
Parents
8307ad3
Tree
18dad63

3 changed files

StatusFile+-
M src/display_module.f90 1 1
M src/fuss_main.f90 27 0
M src/git_module.f90 154 0
src/display_module.f90modified
@@ -169,7 +169,7 @@ contains
169169
                      achar(27) // '[31m✗' // achar(27) // '[0m=modified ' // &
170170
                      achar(27) // '[90m✗' // achar(27) // '[0m=untracked ' // &
171171
                      achar(27) // '[34m↓' // achar(27) // '[0m=incoming'
172
-        print '(A)', 'Keys: j/k/↑/↓:nav | ←/→:nav tree | space:toggle | .:hide-dots | a:stage | u:unstage | S:stage-all | U:unstage-all | x:discard | z:stash | Z:unstash | b:switch | n:new-br | R:del-br | f:fetch | d:diff | r:delete | l:pull | m:commit | M:amend | p:push | t:tag | s:status | q:quit'
172
+        print '(A)', 'Keys: j/k/↑/↓:nav | ←/→:nav tree | space:toggle | .:hide-dots | a:stage | u:unstage | S:stage-all | U:unstage-all | x:discard | z:stash | Z:unstash | b:switch | n:new-br | R:del-br | f:fetch | d:diff | c:view | y:cherry-pick | r:delete | l:pull | m:commit | M:amend | p:push | t:tag | s:status | q:quit'
173173
 
174174
         ! Don't free tree - it's owned by interactive_mode
175175
     end subroutine draw_interactive_tree
src/fuss_main.f90modified
@@ -83,6 +83,8 @@ contains
8383
         print '(A)', '        l               Pull from remote'
8484
         print '(A)', '        f               Fetch from remote'
8585
         print '(A)', '        d               Show diff for file'
86
+        print '(A)', '        c               View file contents (bat/less/cat)'
87
+        print '(A)', '        y               Cherry-pick commit from branch'
8688
         print '(A)', '        x               Discard changes'
8789
         print '(A)', ''
8890
         print '(A)', '    Branches & Stash:'
@@ -409,6 +411,10 @@ contains
409411
                 if (items(selected)%is_file) then
410412
                     call git_diff_file(items(selected)%path, items(selected)%has_incoming)
411413
                 end if
414
+            case ('c')  ! View file contents (cat/bat/less)
415
+                if (items(selected)%is_file) then
416
+                    call view_file(items(selected)%path)
417
+                end if
412418
             case ('r')  ! Remove/delete file
413419
                 if (items(selected)%is_file) then
414420
                     call delete_prompt(items(selected)%path, items(selected)%is_untracked)
@@ -474,6 +480,17 @@ contains
474480
                 call mark_incoming_changes(files, n_files)
475481
                 call build_item_list(files, n_files, items, n_items, tree_root, hide_dotfiles)
476482
                 if (selected > n_items .and. n_items > 0) selected = n_items
483
+            case ('y')  ! Cherry-pick (yank commit)
484
+                call cherry_pick_prompt()
485
+                ! Refresh files after cherry-pick
486
+                if (show_all) then
487
+                    call get_all_files(files, n_files)
488
+                else
489
+                    call get_dirty_files(files, n_files)
490
+                end if
491
+                call mark_incoming_changes(files, n_files)
492
+                call build_item_list(files, n_files, items, n_items, tree_root, hide_dotfiles)
493
+                if (selected > n_items .and. n_items > 0) selected = n_items
477494
             case ('.')  ! Toggle hiding dotfiles and gitignored files
478495
                 hide_dotfiles = .not. hide_dotfiles
479496
                 ! Rebuild item list with new filter
@@ -1145,6 +1162,16 @@ contains
11451162
         call read_key(key)
11461163
     end subroutine stash_pop_apply_prompt
11471164
 
1165
+    subroutine cherry_pick_prompt()
1166
+        logical :: success
1167
+
1168
+        ! Clear screen for cherry-pick
1169
+        call clear_screen()
1170
+
1171
+        ! Call git cherry-pick (handles its own prompts and key wait)
1172
+        call git_cherry_pick(success)
1173
+    end subroutine cherry_pick_prompt
1174
+
11481175
     subroutine branch_create_prompt()
11491176
         character(len=512) :: branch_name
11501177
         logical :: success
src/git_module.f90modified
@@ -1274,6 +1274,160 @@ contains
12741274
         call execute_command_line('stty cbreak -echo < /dev/tty', exitstat=status)
12751275
     end subroutine git_diff_file
12761276
 
1277
+    subroutine view_file(filepath)
1278
+        character(len=*), intent(in) :: filepath
1279
+        character(len=2048) :: command
1280
+        integer :: status
1281
+        logical :: bat_available, less_available
1282
+
1283
+        ! Restore terminal temporarily for pager
1284
+        call execute_command_line('stty sane < /dev/tty', exitstat=status)
1285
+
1286
+        ! Check if bat is available
1287
+        call execute_command_line('command -v bat > /dev/null 2>&1', exitstat=status)
1288
+        bat_available = (status == 0)
1289
+
1290
+        ! Check if less is available
1291
+        call execute_command_line('command -v less > /dev/null 2>&1', exitstat=status)
1292
+        less_available = (status == 0)
1293
+
1294
+        ! Use bat if available (with nice syntax highlighting)
1295
+        if (bat_available) then
1296
+            write(command, '(A,A,A)') 'bat --style=numbers,changes --color=always "', trim(filepath), '"'
1297
+            call execute_command_line(trim(command), exitstat=status)
1298
+        else if (less_available) then
1299
+            ! Fallback to less
1300
+            write(command, '(A,A,A)') 'less "', trim(filepath), '"'
1301
+            call execute_command_line(trim(command), exitstat=status)
1302
+        else
1303
+            ! Final fallback to cat with line numbers
1304
+            write(command, '(A,A,A)') 'cat -n "', trim(filepath), '"'
1305
+            call execute_command_line(trim(command), exitstat=status)
1306
+            ! Pause so user can read
1307
+            print '(A)', ''
1308
+            print '(A)', 'Press any key to continue...'
1309
+            call execute_command_line('read -n 1 -s < /dev/tty', exitstat=status)
1310
+        end if
1311
+
1312
+        ! Re-enable cbreak mode
1313
+        call execute_command_line('stty cbreak -echo < /dev/tty', exitstat=status)
1314
+    end subroutine view_file
1315
+
1316
+    subroutine git_cherry_pick(success)
1317
+        logical, intent(out) :: success
1318
+        integer :: status_code, status
1319
+        character(len=512) :: selected_branch, selected_commit
1320
+        character(len=2048) :: command
1321
+
1322
+        success = .false.
1323
+
1324
+        ! Restore terminal for fzf
1325
+        call execute_command_line('stty sane < /dev/tty', exitstat=status)
1326
+
1327
+        print '(A)', achar(27) // '[1mCherry-pick' // achar(27) // '[0m'
1328
+        print '(A)', ''
1329
+        print '(A)', 'Select source branch:'
1330
+        print '(A)', ''
1331
+
1332
+        ! Step 1: Select branch (exclude current branch)
1333
+        call execute_command_line('(git branch --all | grep -v HEAD | grep -v "^\*" | sed "s/^[* ] //" | ' // &
1334
+                                  'sed "s/remotes\\/origin\\///" | sort -u) | ' // &
1335
+                                  'fzf --height=15 --border=rounded --border-label=" ESC to cancel " ' // &
1336
+                                  '--prompt="Source branch: " ' // &
1337
+                                  '--preview="git log --oneline --graph --color=always {} | head -20" ' // &
1338
+                                  '--preview-window=right:50% > /tmp/fuss_branch_select.txt', &
1339
+                                  exitstat=status_code)
1340
+
1341
+        if (status_code /= 0) then
1342
+            call execute_command_line('rm -f /tmp/fuss_branch_select.txt', exitstat=status)
1343
+            print '(A)', ''
1344
+            print '(A)', 'Operation cancelled.'
1345
+            print '(A)', ''
1346
+            print '(A)', 'Press any key to continue...'
1347
+            call execute_command_line('read -n 1 -s < /dev/tty', exitstat=status)
1348
+            call execute_command_line('stty cbreak -echo < /dev/tty', exitstat=status)
1349
+            return
1350
+        end if
1351
+
1352
+        ! Read selected branch
1353
+        open(unit=99, file='/tmp/fuss_branch_select.txt', status='old', action='read')
1354
+        read(99, '(A)', iostat=status) selected_branch
1355
+        close(99, status='delete')
1356
+
1357
+        if (status /= 0 .or. len_trim(selected_branch) == 0) then
1358
+            print '(A)', ''
1359
+            print '(A)', 'No branch selected.'
1360
+            print '(A)', ''
1361
+            print '(A)', 'Press any key to continue...'
1362
+            call execute_command_line('read -n 1 -s < /dev/tty', exitstat=status)
1363
+            call execute_command_line('stty cbreak -echo < /dev/tty', exitstat=status)
1364
+            return
1365
+        end if
1366
+
1367
+        ! Step 2: Select commit from that branch (only commits NOT in current branch)
1368
+        print '(A)', ''
1369
+        print '(A)', 'Select commit to cherry-pick from: ' // trim(selected_branch)
1370
+        print '(A)', ''
1371
+
1372
+        write(command, '(A,A,A)') '(git log ', trim(selected_branch), ' --not HEAD --oneline --color=always) | ' // &
1373
+                                  'fzf --height=20 --border=rounded --border-label=" ESC to cancel " ' // &
1374
+                                  '--prompt="Commit: " ' // &
1375
+                                  '--preview="git show --color=always {1}" ' // &
1376
+                                  '--preview-window=right:60% > /tmp/fuss_commit_select.txt'
1377
+        call execute_command_line(trim(command), exitstat=status_code)
1378
+
1379
+        if (status_code /= 0) then
1380
+            call execute_command_line('rm -f /tmp/fuss_commit_select.txt', exitstat=status)
1381
+            print '(A)', ''
1382
+            print '(A)', 'Operation cancelled.'
1383
+            print '(A)', ''
1384
+            print '(A)', 'Press any key to continue...'
1385
+            call execute_command_line('read -n 1 -s < /dev/tty', exitstat=status)
1386
+            call execute_command_line('stty cbreak -echo < /dev/tty', exitstat=status)
1387
+            return
1388
+        end if
1389
+
1390
+        ! Read selected commit (first 7 chars is the hash)
1391
+        open(unit=99, file='/tmp/fuss_commit_select.txt', status='old', action='read')
1392
+        read(99, '(A)', iostat=status) selected_commit
1393
+        close(99, status='delete')
1394
+
1395
+        if (status /= 0 .or. len_trim(selected_commit) == 0) then
1396
+            print '(A)', ''
1397
+            print '(A)', 'No commit selected.'
1398
+            print '(A)', ''
1399
+            print '(A)', 'Press any key to continue...'
1400
+            call execute_command_line('read -n 1 -s < /dev/tty', exitstat=status)
1401
+            call execute_command_line('stty cbreak -echo < /dev/tty', exitstat=status)
1402
+            return
1403
+        end if
1404
+
1405
+        ! Extract commit hash (first word)
1406
+        selected_commit = selected_commit(1:7)
1407
+
1408
+        ! Step 3: Perform cherry-pick
1409
+        print '(A)', ''
1410
+        write(command, '(A,A,A)') 'git cherry-pick ', trim(selected_commit), ' 2>&1'
1411
+        call execute_command_line(trim(command), exitstat=status_code)
1412
+
1413
+        print '(A)', ''
1414
+        if (status_code == 0) then
1415
+            print '(A)', achar(27) // '[32m✓ Cherry-picked ' // trim(selected_commit) // achar(27) // '[0m'
1416
+            success = .true.
1417
+        else
1418
+            print '(A)', achar(27) // '[31m✗ Cherry-pick failed or has conflicts' // achar(27) // '[0m'
1419
+            print '(A)', 'Resolve conflicts, then run: git cherry-pick --continue'
1420
+            print '(A)', 'Or abort with: git cherry-pick --abort'
1421
+        end if
1422
+
1423
+        print '(A)', ''
1424
+        print '(A)', 'Press any key to continue...'
1425
+        call execute_command_line('read -n 1 -s < /dev/tty', exitstat=status)
1426
+
1427
+        ! Re-enable cbreak mode
1428
+        call execute_command_line('stty cbreak -echo < /dev/tty', exitstat=status)
1429
+    end subroutine git_cherry_pick
1430
+
12771431
     subroutine git_tag(tag_name, tag_message, success)
12781432
         character(len=*), intent(in) :: tag_name
12791433
         character(len=*), intent(in) :: tag_message