Skip to content
Snippets Groups Projects
Select Git revision
  • master default
  • stable
  • WorkSpace
  • AppImage
  • newdialogs
  • gb.test.tap
  • gb.chart2
  • evdev
  • gb.joystick
  • gb.spreadsheet
  • gb.test
  • gb.net.imap
  • gb.git
  • exclamation-mark-completion
  • cmake
  • 3.20.2
  • 3.20.1
  • 3.20.0
  • 3.19.6
  • 3.19.5
  • 3.19.4
  • 3.19.3
  • 3.19.2
  • 3.19.1
  • 3.19.0
  • 3.18.4
  • 3.18.3
  • 3.18.2
  • 3.18.1
  • 3.18.0
  • 3.17.3
  • 3.17.2
  • 3.17.1
  • 3.17.0
  • 3.16.3
35 results

CVersionControlGit.class

  • Benoît Minisini's avatar
    0d506a34
    [DEVELOPMENT ENVIRONMENT] · 0d506a34
    Benoît Minisini authored
    * BUG: Version control: Correctly trim the identity name and e-mail.
    * NEW: Version control: Display a big label when there is no change to commit.
    * NEW: Version control: Correctly update 'Change' tab when there is no change to commit.
    0d506a34
    History
    [DEVELOPMENT ENVIRONMENT]
    Benoît Minisini authored
    * BUG: Version control: Correctly trim the identity name and e-mail.
    * NEW: Version control: Display a big label when there is no change to commit.
    * NEW: Version control: Correctly update 'Change' tab when there is no change to commit.
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
CVersionControlGit.class 14.49 KiB
' Gambas class file

Inherits CVersionControl
Create Static

Public Const Name As String = "Git"
Public Const LongName As String = "Git"
Public Const DoNotPush As Boolean = True
Public Const CancelUpdate As Boolean = True
Public Const NeedMove As Boolean = False
Public Const CanCreateBranch As Boolean = True
Public Const HasIdentity As Boolean = True

Private $aConflict As String[] = ["DD", "AU", "UD", "UA", "DU", "AA", "UU"]

Private $bHasGit As Boolean
Private $bCheckGit As Boolean
Private $sRoot As String
Private $bCanControl As Boolean
Private $bCanInit As Boolean
Private $bHasRemote As Boolean

' Private $iPadHistory As Integer
' Private $hHighlightDiff As TextHighlighter

Private Sub Init()

  If $bCheckGit Then Return
  
  $bHasGit = System.Exist("git")
  $bCheckGit = True

End

Private Sub RunShell(sCmd As String, Optional bSilent As Boolean) As String
  
  Return VersionControl.Shell("cd " & Shell(Project.Dir) & " && " & sCmd, bSilent)
  
End

Static Public Sub FindRoot(Optional sDir As String) As String

  Dim sPath As String
  Dim sData As String

  If Not sDir Then sDir = Project.Dir
  While Len(sDir) > 1
    sPath = sDir &/ ".git"
    If Exist(sPath) Then
      If Exist(sPath &/ "HEAD") Then Return sDir &/ ".git"
      Try sData = File.Load(sPath)
      If sData Begins "gitdir:" Then Return LTrim(Mid$(sData, 8, -1))
    Endif
    sDir = File.Dir(sDir)
  Wend

End

Public Sub Check() As Boolean
  
  Dim sResult As String
  Dim sRoot As String
  
  Init()
  
  If $bHasGit Then
    
    'sResult = RunShell("git status --porcelain .project")
    Exec ["git", "status", "--porcelain", ".project"] With ["PWD=" & Project.Dir] To sResult
    
    If Process.LastValue Then
      $bCanInit = True
      $bCanControl = False
    Else
      $bCanInit = False
      $bCanControl = sResult Begins "??"

      If Not $bCanControl Then
        sRoot = FindRoot()
        If sRoot Then
          $sRoot = File.Dir(sRoot)
          '$bHasRemote = RunShell("git remote show")
          Exec ["git", "remote", "show"] With ["PWD=" & Project.Dir] To sResult
          $bHasRemote = Trim(sResult)
          Return True
        Endif
      Endif
      
    Endif
    
  Endif
  
End

Public Sub GetCurrentBranch() As String

  ' Works only with git > 2.22
  ' Return Trim(RunShell("git branch --show-current"))
  Return Trim(RunShell("git rev-parse --abbrev-ref HEAD"))
  
End

Public Sub GetBranches(ByRef sCurrent As String) As String[]
  
  Dim sResult As String
  Dim sBranch As String
  Dim aBranches As New String[]
  Dim bCurrent As Boolean
  
  sResult = RunShell("git branch -a", True) 
  
  For Each sBranch In Split(Trim(sResult), "\n")
    
    sBranch = Trim(sBranch)
    If sBranch Begins "*" Then
    
      sBranch = Trim(Mid$(sBranch, 2))
      bCurrent = True
    Else
      bCurrent = False
      If sBranch Begins "+ " Then sBranch = Mid$(sBranch, 3)
    Endif
    
    If sBranch Begins "remotes/origin/" Then
      sBranch = Mid$(sBranch, 16)
      If InStr(sBranch, " -> ") Then Continue
    Endif
    
    If bCurrent Then sCurrent = sBranch 
    
    If Not aBranches.Exist(sBranch) Then aBranches.Add(sBranch)
    
  Next
  
  aBranches.Sort(gb.Natural)
  
  Return aBranches
  
End
Public Sub Diff(sPath As String, Optional bFull As Boolean, bNoWhiteSpace As Boolean) As String
  
  Dim sDiff As String
  Dim sCmd As String
  Dim sHead As String
  Dim sFull As String
  Dim iPos As Integer
  Dim sOption As String

  'If VersionControl.Shell("cd " & Shell(Project.Dir) & " && git log -1", True) Then
  If RunShell("git log -1", True) Then
    sHead = "HEAD"
  Else
    sHead = "--staged"
  Endif

  If bFull Then sFull = " -U100000"
  
  If bNoWhiteSpace Then sOption = " -b --ignore-blank-lines"

  If IsDir(sPath) Then
    sCmd = "cd " & Shell(sPath) & " && git diff" & sOption & sFull & " " & sHead & " ."
  Else
    sCmd = "cd " & Shell(File.Dir(sPath)) & " && git diff" & sOption & sFull & " " & sHead & " " & Shell(File.Name(sPath))
  Endif
  
  sDiff = VersionControl.Shell(sCmd)
  
  If bFull Then
    iPos = InStr(sDiff, "@@\n")
    If iPos Then
      sDiff = Mid$(sDiff, iPos + 3)
    Else
      sDiff = ""
    Endif
  Endif
  
  Return sDiff

End

Public Sub History(sPath As String, Optional sPath2 As String, bFull As Boolean) As String
  
  Dim sCmd As String
  Dim sFull As String
  
  If bFull Then sFull = "-p -b "
  
  If IsDir(sPath) Then
    sCmd = "cd " & Shell(sPath) & " && git log --date=rfc2822 " & sFull & "."
  Else
    sCmd = "cd " & Shell(File.Dir(sPath)) & " && git log --date=rfc2822 " & sFull & Shell(File.Name(sPath))
    If sPath2 Then sCmd &= " " & Shell(File.Name(sPath2))
  Endif
  
  'Return VersionControl.Shell(sCmd)
  Shell sCmd Wait

End

Public Sub Status(Optional sPath As String) As String

  Dim sStatus As String

  If Not sPath Then sPath = Project.Dir
  sStatus = VersionControl.Shell("cd " & Shell$(sPath) & " && git status .", True)
  Return sStatus
  
End
Public Sub GetDefaultJournal() As String

  Dim aStatus As String[]
  Dim I As Integer
  
  aStatus = Split(Status(Project.Dir), "\n")
  For I = 0 To aStatus.Max
    aStatus[I] = "# " & aStatus[I]
  Next
  
  Return "# " & VersionControl.GetDelimChange() & "\n#\n" & aStatus.Join("\n")
  
End

Public Sub Update() As Integer

  Dim aEnv As String[]
  
  aEnv = VersionControl.LANG_ENV.Copy()
  aEnv.Add("PWD=" & Project.Dir)

  VersionControl.Run(["git", "remote", "update", "origin", "--prune"], True, True, aEnv)

  Return VersionControl.Run(["git", "pull", "-q"], True, True, aEnv)

End

' Public Sub UpdatePath(aPath As String[])
' 
'   Dim sStatus As String
'   Dim sCmd As String
'   Dim sPath As String
'   
'   sCmd = "cd " & Shell$(Project.Dir) & " && git status -uall --porcelain -z"
'   For Each sPath In aPath
'     sCmd &= " " & Mid$(sPath, Len(Project.Dir) + 2)
'   Next
'   
'   sStatus = VersionControl.Shell(sCmd, True)
'   UpdatePathFromStatus(sStatus)
' 
' End

Public Sub CheckPaths()

  Dim sStatus As String

  'sStatus = VersionControl.Shell("cd " & Shell$(Project.Dir) & " && git status -uall --porcelain -z .", True)
  sStatus = RunShell("git status -uall --porcelain -z .", True)
  UpdatePathFromStatus(sStatus)

End

Private Sub UpdatePathFromStatus(sStatus As String)

  Dim sFile As String
  Dim sPath As String

  For Each sStatus In Split(sStatus, Chr$(0), "", True)

    sFile = Mid$(sStatus, 4)
    sPath = $sRoot &/ sFile
    If sPath Not Begins Project.Dir Then Continue

    If sStatus Begins "??" Then
      Project.SetFlag(sPath, Project.FLAG_ADDED)
    Else If $aConflict.Exist(Left(sStatus, 2)) Then
      Project.SetFlag(sPath, Project.FLAG_CONFLICT)
    Else If InStr("MDR", Mid$(sStatus, 2, 1)) Or If InStr("AMR", Left(sStatus)) Then
      Project.SetFlag(sPath, Project.FLAG_MODIFIED)
    Endif

  Next

End

Public Sub AddFile(sPath As String, Optional (bForce) As Boolean)

  RunShell("git add " & Shell(sPath))
  
End

Public Sub AddDir((sPath) As String)
  
  ' git does not track directories
  ' AddFile(sPath)
  
End

Public Sub Revert(Optional sPath As String) As Boolean
  
  If sPath Then 
    RunShell("git checkout " & Shell(sPath))
  Else
    RunShell("git checkout " & Shell(Project.Dir))
  Endif
  
End

Public Sub RemoveFile(sPath As String, Optional (bForce) As Boolean)

  RunShell("git rm " & Shell(sPath))
  
End

Public Sub RemoveDir(sPath As String)

  RunShell("git rm -rf --cached " & Shell(sPath))
  Shell.RmDir(sPath)

End

Public Sub Commit(sChange As String, bDoNotPush As Boolean) As Boolean
  
  Dim sFile As String 
  Dim aEnv As String[]
  
  If sChange Then
    sFile = Temp$()
    File.Save(sFile, sChange)
    'VersionControl.Run(["git", "commit", "-F", sFile, Project.Dir], False, False, VersionControl.LANG_ENV)
    RunShell("git commit -F " & Shell$(sFile) & " . 2>&1") ', Project.Dir)
    If Process.LastValue Then Return True
  Endif
  
  If Not bDoNotPush And If $bHasRemote Then
  
    aEnv = VersionControl.LANG_ENV.Copy()
    aEnv.Add("PWD=" & Project.Dir)
    VersionControl.Run(["git", "push"], True, True, aEnv)
    If Process.LastValue Then VersionControl.ShowError(("Unable to push the project."))
    
  Endif
  
End

Public Sub Info() As Collection

  Dim cInfo As New Collection
  Dim aResult As String[]
  Dim sResult As String
  Dim nModif As Integer
  Dim sText As String
  
  cInfo[("Local root path")] = $sRoot
  
  aResult = Split(VersionControl.Shell("cd " & Shell(Project.Dir) & " && git log -n1"), "\n")
  cInfo[("Last commit")] = Trim(Mid$(aResult[0], InStr(aResult[0], " ") + 1))
  cInfo[("Last commit author")] = Trim(Mid$(aResult[1], InStr(aResult[1], " ") + 1))
  cInfo[("Last commit date")] = Trim(Mid$(aResult[2], InStr(aResult[2], " ") + 1))
  
  aResult = Split(RunShell("git branch -vv"), "\n")
  For Each sResult In aResult
    If sResult Begins "*" Then
      aResult = Scan(sResult, "* * * [[]*] *")
      If aResult.Count = 0 Then aResult = Scan(sResult, "* * *")
      cInfo[("Current branch")] = aResult[1]
      If aResult.Count >= 4 Then cInfo[("Origin")] = aResult[3]
      Break
    Endif
  Next
  
  Try nModif = Split(Trim(RunShell("git status --porcelain")), "\n").Count
  If nModif = 0 Then
    sText = ("The current branch is up to date.")
  Else If nModif = 1 Then
    sText = ("One file to commit in the current branch.")
  Else
    sText = Subst(("&1 files to commit in the current branch."), nModif)
  Endif
  
  If nModif Then  
    Try nModif = Split(Trim(RunShell("git status --porcelain " & Shell(Project.Dir))), "\n").Count
    sText &= "\n"
    If nModif = 0 Then
      sText &= ("None in the current project.")
    Else If nModif = 1 Then
      sText &= ("One in the current project.") 
    Else
      sText &= Subst(("&1 in the current project."), nModif)
    Endif
  Endif
  
  cInfo[("Status")] = sText
  
  aResult = Split(RunShell("git remote -vv"), "\n")
  Try cInfo[("Remote repository URL")] = Scan(aResult[0], "* * *")[1]
  
  Return cInfo
  
End

Public Sub SetBranch(sBranch As String)
  
  RunShell("git checkout " & Shell(sBranch) & " 2>&1")
  
End

Public Sub MoveFile(sOld As String, sNew As String)

  RunShell("git mv " & Shell(sOld) & " " & Shell(sNew) & " && git add " & Shell(sNew))
  If Exist(sOld) And If Not Exist(sNew) Then Move sOld To sNew

End

Public Sub MoveDir(sOld As String, sNew As String) As Boolean

  MoveFile(sOld, sNew)

End

Public Sub UpdatePath(sPath As String, Optional sPath2 As String)

  Dim sStatus As String
  Dim sCmd As String

  ' svn status is automatically recursive!
  sCmd = "cd " & Shell$(Project.Dir) & "; git status -uall --porcelain -z " & Shell(Project.MakeRelativePath(sPath))
  If sPath2 Then sCmd &= " " & Shell(Project.MakeRelativePath(sPath2))

  sStatus = VersionControl.Shell(sCmd, True)
  UpdatePathFromStatus(sStatus)

End

Public Sub CanControlProject() As Boolean

  Return $bCanControl Or $bCanInit

End

Public Sub ControlProject() As Boolean
  
  If Not CanControlProject() Then Return True
  
  If $bCanInit Then
    
    If Message.Warning(("A new Git repository will be created for this project."), ("OK"), ("Cancel")) = 2 Then Return
    
    RunShell("git init && git add .")
    If Process.LastValue Then Return True
  
  Else
    
    RunShell("git add .")
  
  Endif
  
End

Public Sub HasRemote() As Boolean
  
  Return $bHasRemote
  
End

Public Sub SolveConflict(sPath As String, Optional iAccept As Integer) As Boolean
  
  If iAccept Then
    Select iAccept
      Case VersionControl.ACCEPT_OURS
        RunShell("git checkout --ours " & Shell$(sPath))
      Case VersionControl.ACCEPT_THEIRS
        RunShell("git checkout --theirs " & Shell$(sPath))
    End Select
    Return Process.LastValue
  Endif
  
  RunShell("git add " & Shell$(sPath))
  If Process.LastValue Then Return True
  
  VersionControl.CheckPaths()
  If Project.HasConflict Then Return
  
  'FVersionControl.Run(True)
  RunShell("git commit -m " & Shell$(("Solve conflict."))) ', Project.Dir)
  
End

' Private Sub FormatDate(sDate As String) As String
' 
'   Dim dDate As Date
' 
'   Try dDate = Date.FromRFC822(sDate)
'   If Not dDate Then Return "?"
'   Return Format(dDate, gb.LongDate)
' 
' End

Public Sub InitHistory(hEditor As TextEditor)
  
  hEditor.Highlight = "commit_git"
  
End

' Fast Public Sub HighlightHistory(Text As String)
'   
'   Dim iState As Integer
'   Dim iLen As Integer
' 
'   iState = TextHighlighter.State
'   iLen = String.Len(Text)
' 
'   TextHighlighter.Limit = False
'   
'   If $iPadHistory = 0 Then
'     $iPadHistory = Max(Max(String.Len(("Date")), String.Len(("Author"))), String.Len("Commit"))
'   Endif
'   
'   TextHighlighter.Alternate = True
'   If Text Begins "commit " Then 
'     TextHighlighter.TextAfter = String.PadRight("Commit", $iPadHistory) & " : " & Mid$(Text, 8)
'     TextHighlighter.Add(Highlight.DataType, String.Len(TextHighlighter.TextAfter))
'     'TextHighlighter.Add(Highlight.DataType, String.Len(Text))
'     TextHighlighter.Limit = True
'   Else If Text Begins "Merge: " Then 
'     TextHighlighter.TextAfter = String.PadRight("Merge", $iPadHistory) & " : " & Mid$(Text, 8)
'     TextHighlighter.Add(Highlight.Number, String.Len(TextHighlighter.TextAfter))
'     'TextHighlighter.Add(Highlight.DataType, String.Len(Text))
'     TextHighlighter.Limit = True
'   Else If Text Begins "Author: " Then
'     TextHighlighter.TextAfter = String.PadRight(("Author"), $iPadHistory) & " : " & Mid$(Text, 9)
'     TextHighlighter.Add(Highlight.Function, String.Len(TextHighlighter.TextAfter))
'     'TextHighlighter.Add(Highlight.Function, String.Len(Text))
'   Else If Text Begins "Date: " Then
'     TextHighlighter.TextAfter = String.PadRight(("Date"), $iPadHistory) & " : " & FormatDate(Mid$(Text, 7))
'     TextHighlighter.Add(Highlight.Preprocessor, String.Len(TextHighlighter.TextAfter))
'     'TextHighlighter.Add(Highlight.Preprocessor, String.Len(Text))
'   Else
'     TextHighlighter.Alternate = False
'     If Not $hHighlightDiff Then $hHighlightDiff = TextHighlighter["diff"]
'     $hHighlightDiff.Run(Text)
'     'TextHighlighter.Add(Highlight.Normal, iLen)
'   Endif
'   
' End

Public Sub GetRoot() As String
  
  Return $sRoot
  
End

Public Sub CreateBranch(sNewBranch As String) As Boolean
  
  RunShell("git branch " & Shell(sNewBranch))
  If Process.LastValue Then Return True
  
End

Public Sub GetIdentity(ByRef sName As String, ByRef sEmail As String)
  
  Try sName = Trim(RunShell("git config user.name"))
  Try sEmail = Trim(RunShell("git config user.email"))
  
End

Public Sub SetIdentity(sName As String, sMail As String)
  
  If sName Then
    RunShell("git config user.name " & Shell$(sName))
  Else
    RunShell("git config user.name --unset")
  Endif
  If sMail Then
    RunShell("git config user.email " & Shell$(sMail))
  Else 
    RunShell("git config user.email --unset")
  Endif
  
End