2015-10-14 39 views
7

Próbuję zaimplementować coś, co da mi wynik git pull lub git fetch && git merge. Częściowo działam, ale problem polega na tym, że po uruchomieniu następującego kodu, , obecne repo zostało po myśli, że istnieją lokalne zmiany do zatwierdzenia.Git2go - pull && merge

O ile widzę, nie sądzę, że prawdopodobnie nie utworzę przypisanego do adnotacji zatwierdzenia z prawidłowego HEAD, czy też muszę dokonać kolejnego zatwierdzenia? (Nie jestem pewien).

Mój kod wygląda tak i jestem zakleszczony:

func (repo *Repo) Pull() error { 
    // Get remote 
    remote, err := repo.Remotes.Lookup("origin") 
    if err != nil { 
     remote, err = repo.Remotes.Create("origin", repo.Path()) 
     if err != nil { 
      return err 
     } 
    } 

    // Get the branch 
    branch, err := repo.Branch() 
    if err != nil { 
     return err 
    } 

    // Get the name 
    branchName, err := branch.Name() 
    if err != nil { 
     return err 
    } 

    if err := remote.Fetch([]string{}, &git.FetchOptions{}, ""); err != nil { 
     return err 
    } 

    // Merge 
    remoteRef, err := repo.References.Lookup("refs/remotes/origin/" + branchName) 
    if err != nil { 
     return err 
    } 

    mergeRemoteHead, err := repo.AnnotatedCommitFromRef(remoteRef) 
    if err != nil { 
     return err 
    } 

    mergeHeads := make([]*git.AnnotatedCommit, 1) 
    mergeHeads[0] = mergeRemoteHead 
    if err = repo.Merge(mergeHeads, nil, nil); err != nil { 
     return err 
    } 

    return nil 
} 

Po uruchomieniu go, otrzymuję zmiany ze zdalnego połączone i katalog roboczy aktualizowany, ale to mówi mi, że trzeba wykonać commit.

Myślę, że mam podobny problem jak OP this question.

Odpowiedz

7

To git_merge funkcję drodze libgit2 jest udokumentowana:

Wszelkie zmiany są wystawiane do popełniania a wszelkie konflikty są zapisywane w indeksie. Dzwoniący powinni sprawdzić indeks repozytorium po jego zakończeniu, rozwiązać wszelkie konflikty i przygotować zatwierdzenie.

Musisz więc dodać kod, aby sprawdzić, czy index has conflicts. Jeśli ich nie ma, możesz commit, co jest wystawiane. W przeciwnym razie prawdopodobnie chciałbyś poprosić użytkownika o rozwiązanie konfliktów.

+0

Czy mógłbyś nieco wyjaśnić, dlaczego muszę utworzyć zatwierdzenie? Są to nadchodzące zmiany z "pochodzenia". Czy powinny być wystawiane? Nie powinien tylko odtwarzać tych zmian i zostawić mi czysty katalog roboczy (biorąc pod uwagę, że nie wprowadziliśmy żadnych zmian, które wprowadziliśmy lokalnie). Ta funkcja wprowadza zmiany, które nie zostały wprowadzone lokalnie, i nie zapisuje zatwierdzeń wycofanych ze zdalnego. – Sthe

+0

libgit2 (i jego powiązania) działają na niższym poziomie niż linia poleceń git (porcelana). W szczególności, 'Merge' git2go robi o wiele mniej niż' git merge'. Podczas gdy 'git merge' domyślnie jest w stanie uprościć szybkie przewijanie do przodu (które w rzeczywistości wcale nie są scalone, patrz' --ff'), 'Merge' git2go jawnie tworzy scalenie, nawet jeśli fast-forward był możliwy. Merge z kolei zawsze tworzy zatwierdzenie scalenia, a aby coś zatwierdzić, indeks musi zostać wypełniony. Znowu "Scalenie" git2go jest znacznie bardziej niskopoziomowe i wymaga jawnego zatwierdzenia, nawet jeśli nie ma konfliktów. – sschuberth

3
Korzystając z powyższej odpowiedzi z sschuberth, udało mi się stworzyć działającą wersję tej funkcji i pomyślałem, że podzielę się tym przełomem. Jak wskazano, funkcja repo.Merge() w libgit2 (jak w tym przypadku Git2go) nie robi tak wiele, jak robi to git pull. Pozwól mi wyjaśnić to jeden krok na raz (stoję poprawione):

Jak wyjaśniono here, git pull faktycznie robi git fetch a następnie git merge i to, co zrobimy:

Zlokalizuj pilota do pobrania zmiany od

remote, err := repo.Remotes.Lookup("origin") 
if err != nil { 
    return err 
} 

Fetch zmian z zdalnego

if err := remote.Fetch([]string{}, nil, ""); err != nil { 
    return err 
} 

Uzyskaj odpowiedni zdalnego adresowania

remoteBranch, err := repo.References.Lookup("refs/remotes/origin/branch_name") 
if err != nil { 
    return err 
} 

Masz teraz zmiany z pilota, ale trzeba pozwolić Git powiedzieć, jak radzić sobie z nimi dalej. Tak więc, wykonaj analizę scalania.

Przeprowadzić analizę Merge

annotatedCommit, err := repo.AnnotatedCommitFromRef(remoteBranch) 
if err != nil { 
    return err 
} 

// Do the merge analysis 
mergeHeads := make([]*git.AnnotatedCommit, 1) 
mergeHeads[0] = annotatedCommit 
analysis, _, err := repo.MergeAnalysis(mergeHeads) 
if err != nil { 
    return err 
} 

Teraz trzeba sprawdzić wartość analysis aby zobaczyć, które status value to wskazuje i zrobić scalania odpowiednio.

test zwrócona wartość

Pamiętaj, aby zrobić test na poziomie binarnym, więc używać bitowe operatorów. Na przykład:

if analysis & git.MergeAnalysisUpToDate != 0 { 
    return nil 
} 

Nie ma tu nic do roboty (w moim przypadku). Wszystko jest aktualne.

else if analysis & git.MergeAnalysisNormal != 0 { 
    // Just merge changes 
    if err := repo.Merge([]*git.AnnotatedCommit{annotatedCommit}, nil, nil); err != nil { 
     return err 
    } 
    // Check for conflicts 
    index, err := repo.Index() 
    if err != nil { 
     return err 
    } 

    if index.HasConflicts() { 
     return errors.New("Conflicts encountered. Please resolve them.") 
    } 

    // Make the merge commit 
    sig, err := repo.DefaultSignature() 
    if err != nil { 
     return err 
    } 

    // Get Write Tree 
    treeId, err := index.WriteTree() 
    if err != nil { 
     return err 
    } 

    tree, err := repo.LookupTree(treeId) 
    if err != nil { 
     return err 
    } 

    localCommit, err := repo.LookupCommit(head.Target()) 
    if err != nil { 
     return err 
    } 

    remoteCommit, err := repo.LookupCommit(remoteBranchID) 
    if err != nil { 
     return err 
    } 

    repo.CreateCommit("HEAD", sig, sig, "", tree, localCommit, remoteCommit) 

    // Clean up 
    repo.StateCleanup() 
} 

W skrócie, powyższy blok kodu wykonuje scalanie i testuje konflikty po. Jeśli napotkasz jakiekolwiek konflikty, radzę sobie z nimi (prawdopodobnie zachęcam użytkownika). Spowoduje to niezatwierdzone zmiany, więc pamiętaj o utworzeniu zatwierdzenia po.

else if analysis & git.MergeAnalysisFastForward != 0 { 
    // Fast-forward changes 
    // Get remote tree 
    remoteTree, err := repo.LookupTree(remoteBranchID) 
    if err != nil { 
     return err 
    } 

    // Checkout 
    if err := repo.CheckoutTree(remoteTree, nil); err != nil { 
     return err 
    } 

    branchRef, err := repo.References.Lookup("refs/heads/branch_name") 
    if err != nil { 
     return err 
    } 

    // Point branch to the object 
    branchRef.SetTarget(remoteBranchID, "") 
    if _, err := head.SetTarget(remoteBranchID, ""); err != nil { 
     return err 
    } 

} 

W powyższym kodzie nie ma nic do scalenia. Trzeba tylko powtórzyć zmiany ze zdalnego na lokalny i zaktualizować tam, gdzie wskazuje HEAD.

Powyższe było dla mnie wystarczające. Mam nadzieję, że to podejście również ci pomoże. Znajdź pełną funkcję na this gist