Git2go:如何处理简单的合并冲突

时间:2016-05-06 12:35:31

标签: git go commit conflict libgit2

对于一个简单的实时编码环境,我编写了一个git add,commit和push函数,当两个用户在文件的同一部分进行编码时,除了冲突的情况之外,它正常工作。它正确执行冲突分析并加载结果:

// Live-Editor, Processing / Javascript
fill(130, 52, 130);
<<<<<<< HEAD
textSize(130);
=======
textSize(120);
>>>>>>> master
text( "",160, 100);

显示git样式的差异。但是当我在编辑器中进行更改并再次添加/ commit / push时,它会导致相同的冲突。就像它没有记住&#34;冲突已经存在并再次出现。 (与git命令行行为不同。)

这是我的代码(我将错误处理用于缩短...)

// Add
repo, err := git.OpenRepository(dir)
index, err := repo.Index()
err = index.AddAll([]string{}, git.IndexAddDefault, nil)
err = index.Write()  
treeId, err := index.WriteTreeTo(repo)
tree, err := repo.LookupTree(treeId)

// Commit
sig := &git.Signature{
    Name:  userName,
    Email: userName + "@" + beego.AppConfig.String("userdata::emailserver"),
    When:  time.Now(),
}
var currentCommit *git.Oid
if firstCommit == true {
    _, err = repo.CreateCommit("HEAD", sig, sig, message, tree)
} else {
    currentBranch, err := repo.Head()
    currentTip, err := repo.LookupCommit(currentBranch.Target())
    currentCommit, err = repo.CreateCommit("HEAD", sig, sig, message, tree, currentTip)
}

remote, err := repo.LookupRemote("origin")

// Pull
if firstCommit == false {
    // Pull possible changes from remote repo
    err = remote.Fetch([]string{}, nil, "")
    remoteBranch, err := repo.LookupReference("refs/remotes/origin/master")
    annotatedCommit, err := repo.AnnotatedCommitFromRef(remoteBranch)

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

    if analysis&git.MergeAnalysisUpToDate == 0 && analysis&git.MergeAnalysisNormal != 0 {

        err := repo.Merge([]*git.AnnotatedCommit{annotatedCommit}, nil, nil)

        // Check for conflicts
        index, err := repo.Index()    
        if index.HasConflicts() {
            err = index.Write()
            return errors.New("Conflicts")
        }

        // No unsolvable conflicts, commit it
        treeId, err := index.WriteTree()
        tree, err := repo.LookupTree(treeId)
        localCommit, err := repo.LookupCommit(currentCommit)
        remoteCommit, err := repo.LookupCommit(remoteBranch.Target())
        repo.CreateCommit("HEAD", sig, sig, "Merge commit", tree, localCommit, remoteCommit)
        repo.StateCleanup()
    }
}

// Push
err = remote.Push([]string{"refs/heads/master"}, nil, sig, message)

我认为关键部分是在// Check for conflicts之后,某种程度上让git目录处于允许它执行相同分析的状态。我想在用户进行更改并再次提交项目后不进行分析,但有可能在此期间另一个用户已经在远程仓库中更改了某些内容。

如何通过git2go处理此类冲突的好方法?

1 个答案:

答案 0 :(得分:1)

我解决了这个问题。碰撞检测工作正常。如果出现不可自动解决的冲突,则将控制权交还给用户。他进行了更改,并且在冲突分析再次发生之前,这些更改尚未完成。

这是完整的代码(&#34之后的部分;合并提交(如果 - 现在希望解决 - 冲突)至关重要。&#34;):

//////////////////////////////////////////////////////////
// GitAddCommitPush
func GitAddCommitPush(userName string, dir string, message string, firstCommit bool) error {

    ///////////////////////////////////////////////////////////////////////
    // Add
    //
    // 1 Open repository
    repo, err := git.OpenRepository(dir)
    if err != nil {
        beego.Error("OpenRepository - ", err)
    }

    // 2 Retrieve index
    index, err := repo.Index()
    if err != nil {
        beego.Error("Index - ", err)
    }

    // 3 Remember if we had conflicts before we added everything to the index
    indexHadConflicts := index.HasConflicts()

    // 4 Add everything to the index
    err = index.AddAll([]string{}, git.IndexAddDefault, nil)
    if err != nil {
        beego.Error("AddAll - ", err)
    }

    // 5 Write the index (so git knows about it)
    err = index.Write()
    if err != nil {
        beego.Error("Write - ", err)
    }

    // 6 Write the current index tree to the repo
    treeId, err := index.WriteTreeTo(repo)
    if err != nil {
        beego.Error("WriteTreeTo - ", err)
    }

    /////////////////////////////////////////////////////////////////////////////////////////////
    // Commit
    //
    // 1 Retrieve the tree we just wrote (git's reference of it that it made in the last step)
    tree, err := repo.LookupTree(treeId)
    if err != nil {
        beego.Error("LookupTree - ", err)
    }

    // 2 Create a signature
    sig := &git.Signature{
        Name:  userName,
        Email: userName + "@" + beego.AppConfig.String("userdata::emailserver"),
        When:  time.Now(),
    }

    // 3 Get remote now (as we need it for both, fetch and later push )
    remote, err := repo.LookupRemote("origin")
    if err != nil {
        remote, err = repo.CreateRemote("origin", repo.Path())
        if err != nil {
            beego.Error("CreateRemote - ", err)
        }
    }

    // 4 Read the remote branch
    remoteBranch, err := repo.LookupReference("refs/remotes/origin/master")
    if err != nil {
        beego.Error("Fetch 2 - ", err)
    }

    // 5 Determine if this is a first commit ...
    if firstCommit == true {

        // 5a ... then create a new one
        _, err = repo.CreateCommit("HEAD", sig, sig, message, tree)

    } else {

        // 5b ... or retrieve current head
        currentBranch, err := repo.Head()
        if err != nil {
            beego.Error("Head - ", err)
        }

        // 6 Retrieve current commit
        currentTip, err := repo.LookupCommit(currentBranch.Target())
        if err != nil {
            beego.Error("LookupCommit - ", err)
        }

        // 7 Create a new one on top
        currentCommit, err := repo.CreateCommit("HEAD", sig, sig, message, tree, currentTip)
        if err != nil {
            beego.Error("CreateCommit - ", err)
        }

        ////////////////////////////////////////////////////////////////////////////////////
        // Merge commit (in case of -- now hopefully resolved -- conflicts)
        //
        // 1 If there were conflicts, do the merge commit
        if indexHadConflicts == true {

            // 2 Retrieve the local commit
            localCommit, err := repo.LookupCommit(currentCommit)
            if err != nil {
                beego.Error("Fetch 11 - ", err)
            }

            // 3 Retrieve the remote commit
            remoteCommit, err := repo.LookupCommit(remoteBranch.Target())
            if err != nil {
                beego.Error("Fetch 12 - ", err)
            }

            // 4 Create a new one
            repo.CreateCommit("HEAD", sig, sig, "Merge commit", tree, localCommit, remoteCommit)

            // 5 Clean up
            repo.StateCleanup()
        }

        ///////////////////////////////////////////////////////////////////////////////////
        // Pull (Fetch and Commit)
        //
        // 1 Fetch it (pull without commit)
        err = remote.Fetch([]string{}, nil, "")
        if err != nil {
            beego.Error("Fetch 1 - ", err)
        }

        // 2 Perform an annotated commit
        annotatedCommit, err := repo.AnnotatedCommitFromRef(remoteBranch)
        if err != nil {
            beego.Error("Fetch 3 - ", err)
        }

        // 3 Do the merge analysis
        mergeHeads := make([]*git.AnnotatedCommit, 1)
        mergeHeads[0] = annotatedCommit
        analysis, _, err := repo.MergeAnalysis(mergeHeads)
        if err != nil {
            beego.Error("Fetch 4 - ", err)
        }

        // 4 Check if something happend
        if analysis&git.MergeAnalysisUpToDate == 0 && analysis&git.MergeAnalysisNormal != 0 {

            // 5 Yes! First just merge changes
            if err := repo.Merge([]*git.AnnotatedCommit{annotatedCommit}, nil, nil); err != nil {
                beego.Error("Fetch 5 - ", err)
            }

            // 6 Retrieve the index after that treatment
            index, err := repo.Index()
            if err != nil {
                beego.Error("Fetch 6 - ", err)
            }

            // 7 Check for conflicts
            if index.HasConflicts() {

                // 7a There are not automaticly solvable conflicts ... give them back to the user
                beego.Trace("Conflicts! Write new index and return.", index)
                err = index.Write()
                if err != nil {
                    beego.Error("Write - ", err)
                }

                return errors.New("Conflicts")
            }

            // 8 Write the new tree
            treeId, err := index.WriteTree()
            if err != nil {
                beego.Error("Fetch 9 - ", err)
            }

            // 9 Retrieve the new tree
            tree, err := repo.LookupTree(treeId)
            if err != nil {
                beego.Error("Fetch 10 - ", err)
            }

            // 10 Retrieve the local commit
            localCommit, err := repo.LookupCommit(currentCommit)
            if err != nil {
                beego.Error("Fetch 11 - ", err)
            }

            // 11 Retrieve the remote commit
            remoteCommit, err := repo.LookupCommit(remoteBranch.Target())
            if err != nil {
                beego.Error("Fetch 12 - ", err)
            }

            // 12 Create a new one
            repo.CreateCommit("HEAD", sig, sig, "Merge commit", tree, localCommit, remoteCommit)

            // 13 Clean up
            repo.StateCleanup()
        }
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////
    // Push
    err = remote.Push([]string{"refs/heads/master"}, nil, sig, message)
    if err != nil {
        beego.Error("Push - ", err)
    }

    return err
}