2015-10-14 25 views
7

Sto cercando di implementare qualcosa che mi darà il risultato di git pull o git fetch && git merge. Ho ottenuto questo lavoro in parte, ma il problema che sto avendo è che dopo aver eseguito il seguente codice, il repository corrente viene lasciato a pensare che ci sono modifiche locali da impegnare.Git2go - pull && unione

Per quanto posso vedere, credo che probabilmente non sto creando il commit annotato dal HEAD corretto o ho bisogno di fare un altro commit? (Non sono sicuro).

Il mio codice simile a questo e io sono bloccato:

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 
} 

Dopo l'esecuzione, ottengo le modifiche da remoto dalla fusione e la directory di lavoro aggiornati, ma mi dice che ho bisogno di fare un commit.

Penso di avere un problema simile all'OP di this question.

risposta

7

Questo è il modo in cui la funzione del libgit2 git_merge è documentato:

Tutte le modifiche sono in scena per commettere e gli eventuali conflitti sono scritti all'indice. I chiamanti dovrebbero ispezionare l'indice del repository dopo che questo è stato completato, risolvere eventuali conflitti e preparare un commit.

Quindi è necessario aggiungere il codice per verificare se il index has conflicts. Se non ce ne sono, è possibile effettuare lo commit. Altrimenti probabilmente vorrai chiedere all'utente di risolvere i conflitti.

+0

Potresti elaborare un po 'sul motivo per cui devo creare un commit? Queste sono le modifiche in arrivo da 'origine'. Dovrebbero essere messi in scena? Non dovrebbe semplicemente riprodurre queste modifiche e lasciarmi con una directory di lavoro pulita (dato che non abbiamo apportato modifiche localmente). Questa funzione mette in scena le modifiche non apportate localmente e non registra i commit estratti dal telecomando. – Sthe

+0

libgit2 (e le sue associazioni) operano su un livello inferiore rispetto alla linea di comando git (porcellana). In particolare, il 'Merge' di git2go fa molto meno che' git merge'. Mentre 'git merge' di default è in grado di semplificare le unioni veloci (che di fatto non sono affatto unioni, si veda' --ff'), 'Merge' di git2go crea esplicitamente un'unione, anche se un avanzamento veloce era possibile. Un'unione a sua volta crea sempre un merge commit e, al fine di eseguire il commit di qualcosa, l'indice deve essere compilato. Ancora una volta Git2go's Merge è molto più di basso livello e richiede di impegnarsi esplicitamente anche se non ci sono conflitti. – sschuberth

3

Utilizzando la risposta di sschuberth sopra, sono riuscito a mettere insieme una versione funzionante di questa funzione e ho pensato di condividere l'innovazione. Come sottolineato, la funzione repo.Merge() in libgit2 (come Git2go in questo caso) non fa quasi quanto lo fa git pull. Mi spiego un passo alla volta (mi correggo):

Come spiegato here, git pull in realtà fa un git fetch e poi un git merge ed è quello che faremo:

Individuare il telecomando per recuperare cambia da

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

Fetch cambia da remoto

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

Prendi il corrispondente riferimento remoto

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

Ora avete le modifiche dal remota, ma è necessario far Git dirvi come trattare con loro più. Quindi, fai un'analisi di fusione.

eseguire un'analisi fusione

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 
} 

Ora, è necessario controllare il valore di analysis per vedere quale status value che punti a fare e l'unione di conseguenza.

test il valore restituito

Assicurarsi di fare il test a livello binario, in modo da utilizzare gli operatori bit per bit. Ad esempio:

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

Non c'è niente da fare qui (nel mio caso). Tutto è aggiornato.

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() 
} 

In breve, il blocco di codice sopra esegue solo un'unione e verifica i conflitti dopo. Se si verificano conflitti, trattarli (chiedere all'utente forse). Ciò comporterà modifiche non vincolate, quindi assicurati di creare un commit dopo.

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 
    } 

} 

Nel codice sopra, non c'è nulla da unire. Hai solo bisogno di riprodurre le modifiche da remoto sul tuo locale e aggiornato dove HEAD sta puntando.

Quanto sopra è stato sufficiente per me. Spero che l'approccio ti aiuti anche tu. Trova la funzione completa su this gist