Page 1 of 1

Git Branch watcher

Posted: Tue Feb 21, 2012 5:27 pm
by Kukulkan
Hi,

I just needed a tool that shows me the current branch of my GIT repositories. I think it may be helpful for some of you.

Currently running only on Windows, because of some API tricks to make opaque windows. Would be great to have this for Linux, too :)

Compile and call it like this (I use a shortcut):
GitBranchWatch.exe "RepoFolder" "GITexecutable"
The RepoFolder must be the folder your GIT folders are inside.
GITexecutable is the complete path of your git executable.

Example:
GitBranchWatch.exe "c:\git_sources" "C:\Programme\msysgit\msysgit\cmd\git.cmd"

Code: Select all

; GIT Branch watch
; PB 4.51 / Windows / threadsafe / singlebyte

EnableExplicit

; --- SOME SETTINGS --- 
#WindowWidth = 100 ; width of the repo windows
#FontSize = 6 ; font size for repo info
Global globalColorChanged = RGB(255,255,240)
Global globalColorMaster = RGB(150,255,150)

;  --- structure and repo-array definition --- 
Structure structRepo
  Folder.s
  Name.s
  WinID.i
  LabelID.i
  Branch.s
  LastWindowOffset.i
  LastBranch.s
EndStructure

Global Dim Repositorys.structRepo(100)

;  --- check threadsave compiler switch --- 
CompilerIf #PB_Compiler_Thread = 0
  MessageRequester("GitBranchWatch", "Need to compile threadsafe!")
  End
CompilerEndIf

;  --- CHECK COMMANDLINE PARAMETERS --- 
Global RepositoryFolder.s = ProgramParameter()
Global GitCommandline.s = ProgramParameter()

If RepositoryFolder.s = "" Or GitCommandline.s = ""
  MessageRequester("GitBranchWatch", "Please give me RepoFolder and git commandline as parameters!")
  End
EndIf

If Right(RepositoryFolder.s, 1) <> "\"
  RepositoryFolder.s = RepositoryFolder.s + "\"
EndIf

If FileSize(GitCommandline.s) < 1
  MessageRequester("GitBranchWatch", "Can not find the given GIT executable!")
  End
EndIf

ExamineDesktops()

; =========================================
; THREAD to actualize the repository status
; =========================================
Procedure ThreadRefresh(Offset)
  Protected x, Compiler, Output.s, Branch.s
  
  Repeat
    For x = Offset To 99 Step 2
      If Repositorys(x)\Folder <> ""
        ; Debug "GetRepo status of repo <" + Repositorys(x)\Name + ">"
        
        SetCurrentDirectory(Repositorys(x)\Folder)
    
        Compiler = RunProgram(GitCommandline.s, "status", "", #PB_Program_Open|#PB_Program_Read|#PB_Program_Hide)
        
        Output.s = ""
        If Compiler  
          While ProgramRunning(Compiler)
            If AvailableProgramOutput(Compiler)
              Output.s + ReadProgramString(Compiler) + Chr(10)
            EndIf
          Wend
          Output.s + Chr(10)
          
          CloseProgram(Compiler)
        Else
          Debug "can not run GIT executable with 'status' in folder " + Repositorys(x)\Folder
        EndIf
        
        Output.s + Chr(10)
        ; Debug Output
        
        Protected Pos = FindString(Output.s, "# On branch", 1)
        If Pos > 0
          Protected EndPos = FindString(Output.s, Chr(10), Pos)
          
          Branch.s = Mid(Output.s, Pos, EndPos - Pos)
          Branch.s = Trim(Mid(Branch.s, 12))
        Else
          Branch = "err"
        EndIf
        
        Repositorys(x)\Branch = Branch.s
        
        Delay(10)
        
      EndIf
    Next
    ; wait a gui loop of 10 seconds
    Delay(10000)
  ForEver
EndProcedure

; =========================================
; get available repositorys from folder
;{=========================================
Define Counter.l = 0
If ExamineDirectory(0, RepositoryFolder.s, "*.*")

  While NextDirectoryEntry(0)

    Define FileName.s = DirectoryEntryName(0)
    If DirectoryEntryType(0) = #PB_DirectoryEntry_Directory And FileName.s <> "." And FileName.s <> ".."
      Repositorys(Counter.l)\Folder = RepositoryFolder.s + FileName.s
      Repositorys(Counter.l)\Name = FileName.s
      Repositorys(Counter.l)\WinID = 0
      Repositorys(Counter.l)\LabelID = 0
      ; Debug "Add Repo " + FileName.s + " -> " + Repositorys(Counter.l)\Folder
      Counter.l = Counter.l + 1
    EndIf
    
  Wend
  
Else
  MessageRequester("GitBranchWatch","Can't examine this directory: " + RepositoryFolder.s)
  End
EndIf
;}

; =========================================
; create window for each repository
;{=========================================
Define FontID = LoadFont(#PB_Any, "Microsoft Sans Serif", #FontSize)
Counter.l = 0
Define x
For x = 0 To 99
  ; create window
  If Repositorys(x)\Folder <> ""
    Define WinPos = DesktopWidth(0) - 2* #WindowWidth - (x * (#WindowWidth + 4))
    Repositorys(x)\WinID = OpenWindow(#PB_Any, WinPos, 4, #WindowWidth, 20, "", #PB_Window_BorderLess | #PB_Window_Invisible)
    Repositorys(x)\LabelID = TextGadget(#PB_Any, 0, 0, #WindowWidth, 20, Repositorys(x)\Name + #CR$ + "-", #PB_Text_Center)
    SetWindowColor(Repositorys(x)\WinID, globalColorChanged)
    SetGadgetColor(Repositorys(x)\LabelID, #PB_Gadget_BackColor, globalColorChanged)
    SetGadgetFont(Repositorys(x)\LabelID, FontID(FontID))
    
    SetWindowLong_(WindowID(Repositorys(x)\WinID),-20,GetWindowLong_(WindowID(Repositorys(x)\WinID),-20)|#WS_EX_TOOLWINDOW)
    HideWindow(Repositorys(x)\WinID, 0)
    
    AddKeyboardShortcut(Repositorys(x)\WinID, #PB_Shortcut_Escape, 101)
    
    ; stay topmost
    StickyWindow(Repositorys(x)\WinID, 1)
    ; make layered to get transparent later
    SetWindowLong_(WindowID(Repositorys(x)\WinID), #GWL_EXSTYLE, GetWindowLong_(WindowID(Repositorys(x)\WinID), #GWL_EXSTYLE) | #WS_EX_LAYERED);
  EndIf
Next
;}

Define Quit = #False

; =========================================
; Start two threads to get status.
; One thread for the even and one for the
; odd numbers.
; =========================================
Define ThreadID1 = CreateThread(@ThreadRefresh(), 0)
ThreadPriority(ThreadID1, 10) ; set lower priority

Define ThreadID2 = CreateThread(@ThreadRefresh(), 1)
ThreadPriority(ThreadID2, 10) ; set lower priority

; =========================================
; Normal GUI loop
;{=========================================
Repeat
  
  Repeat
    Define Event    = WaitWindowEvent(50)
    Define EventWin = EventWindow()

    If Event = #PB_Event_CloseWindow  ; If the user has pressed on the close button
      Quit = #True
    EndIf
    
    If Event = #PB_Event_Menu
      Select EventMenu()
         Case 101
          Quit = #True
      EndSelect
    EndIf
    
    Define MouseX = DesktopMouseX()
    Define MouseY = DesktopMouseY()
    
    For x = 0 To 99
      If Repositorys(x)\Folder <> ""
        Define DistanceX = MouseX - WindowX(Repositorys(x)\WinID) - WindowWidth(Repositorys(x)\WinID) / 2
        Define DistanceY = MouseY
        
        Define Distance = Sqr(DistanceX * DistanceX + DistanceY * DistanceY)
        
        Define Opacity = Distance
        If Opacity > 255: Opacity = 255: EndIf
        If Opacity < 0: Opacity = 0: EndIf
        
        SetLayeredWindowAttributes_(WindowID(Repositorys(x)\WinID), 0, Opacity, 2)
        
        Define WindowOffset = Opacity / 20 - 10
        If WindowOffset <> Repositorys(x)\LastWindowOffset
          ; move window only, if position changed
          ResizeWindow(Repositorys(x)\WinID, #PB_Ignore, WindowOffset, #PB_Ignore, #PB_Ignore)
          Repositorys(x)\LastWindowOffset = WindowOffset
        EndIf
        
        If Repositorys(x)\Branch <> Repositorys(x)\LastBranch
          If Repositorys(x)\Branch = "master"
            SetWindowColor(Repositorys(x)\WinID, globalColorMaster)
            SetGadgetColor(Repositorys(x)\LabelID, #PB_Gadget_BackColor, globalColorMaster)
          Else
            SetWindowColor(Repositorys(x)\WinID, globalColorChanged)
            SetGadgetColor(Repositorys(x)\LabelID, #PB_Gadget_BackColor, globalColorChanged)
          EndIf
          Repositorys(x)\LastBranch = Repositorys(x)\Branch
        EndIf
        
        SetGadgetText(Repositorys(x)\LabelID, Repositorys(x)\Name + ":" + #CR$ + Repositorys(x)\Branch)
        
      EndIf
    Next
  Until Quit = #True
  
Until Quit = #True
;}

; =========================================
; Cleanup program.
;{=========================================
KillThread(ThreadID1)
KillThread(ThreadID2)

For x = 0 To 99
  If Repositorys(x)\Folder <> ""
    CloseWindow(Repositorys(x)\WinID)
  EndIf
Next

FreeFont(FontID)
;}
End
It's just quick and dirty and not really clean source, but I just needed about 4 hours to do and some 2 hours to optimize (threads etc.).

It is limited to a maximum of 100 repositories, but mostly your screen will be the limit (on my 1680 pixel screen, the limit is about 15 repositories). It actualizes the state about every 10 seconds. Because of tasks, it benefits from multi-core processors and hyper-threading.

Kukulkan

Re: Git Branch watcher

Posted: Wed Feb 22, 2012 6:42 pm
by Kukulkan
Ah, I forgot to mention:
In order to shutdown, move the mouse pointer to the upper border of your screen and click on one of the hiding windows (yes, it is possible). After it has focus, you simply need to press the ESC key to close the program.

Kukulkan