开发者

How do you detect which files were manually merged in mercurial's history?

I'm part of a team newly using mercurial and we've identified that when merges occur there are many more errors in files that are manually merged. Is it possible from the mercurial logs (i.e. after someone has done the开发者_高级运维 merge and pushed the merge changeset to the central repository) to detect which files were manually merged?


Note, that I have no idea if this is foolproof. Also, it requires a copy of my as of yet unfinished Mercurial library for .NET, probably only runs on Windows, and is kinda rough around the edges.

Note: I'm assuming that by "were manually merged", you mean "files Mercurial didn't automatically merge for us"

Such a file could still be merged somewhat or completely automatic by an external tool, but if the above assumption is good enough, read on.

However, what I did was to effectively run through all the merges in a test repository, re-doing the merges and asking Mercurial to use only its internal merge tool, which leaves files unresolved if they cannot be automatically merged by Mercurial, and then report all unresolved files, clean up the merge, and move on to the next merge changeset.

The library you need (only in source code form at the moment, I told you it was unfinished):

  • Mercurial.Net (my open source project, hosted on CodePlex)

I've attached a zip file at the bottom with everything, test repository, script, and binary copy of library.

The script (I used LINQPad to write and test this, output from a test-repository follows):

void Main()
{
    var repo = new Repository(@"c:\temp\repo");
    var mergeChangesets = repo.Log(new LogCommand()
        .WithAdditionalArgument("-r")
        .WithAdditionalArgument("merge()")).Reverse().ToArray();

    foreach (var merge in mergeChangesets)
    {
        Debug.WriteLine("analyzing merge #" + merge.RevisionNumber +
            " between revisions #" + merge.LeftParentRevision +
            " and #" + merge.RightParentRevision);
        // update to left parent
        repo.Update(merge.LeftParentHash);
        try
        {
            // perform merge with right parent
            var mergeCmd = new MergeCommand();
            mergeCmd.WithRevision = merge.RightParentHash;
            repo.Execute(mergeCmd);

            // get list of unresolved files
            var resolveCmd = new ResolveCommand();
            repo.Execute(resolveCmd);
            var unresolvedFiles = new List<string>();
            using (var reader = new StringReader(resolveCmd.RawStandardOutput))
            {
                string line;
                while ((line = reader.ReadLine()) != null)
                    if (line.StartsWith("U "))
                        unresolvedFiles.Add(line.Substring(2));
            }

            // report
            if (unresolvedFiles.Count > 0)
            {
                Debug.WriteLine("merge changeset #" + merge.RevisionNumber +
                    " between revisions #" + merge.LeftParentRevision +
                    " and #" + merge.RightParentRevision + " had " +
                    unresolvedFiles.Count + " unresolved file(s)");
                foreach (string filename in unresolvedFiles)
                {
                    Debug.WriteLine("  " + filename);
                }
            }
        }
        finally
        {
            // get repository back to proper state
            repo.Update(merge.LeftParentHash, new UpdateCommand().WithClean());
        }
    }
}

public class MergeCommand : MercurialCommandBase<MergeCommand>
{
    public MergeCommand()
        : base("merge")
    {
    }

    [NullableArgumentAttribute(NonNullOption = "--rev")]
    public RevSpec WithRevision
    {
        get;
        set;
    }

    public override IEnumerable<string> Arguments
    {
        get
        {
            foreach (var arg in base.Arguments)
                yield return arg;
            yield return "--config";
            yield return "ui.merge=internal:merge";
        }
    }

    protected override void ThrowOnUnsuccessfulExecution(int exitCode,
        string standardOutput, string standardErrorOutput)
    {
        if (exitCode != 0 && exitCode != 1)
            base.ThrowOnUnsuccessfulExecution(exitCode, standardOutput,
                standardErrorOutput);
    }
}

public class ResolveCommand : MercurialCommandBase<MergeCommand>
{
    public ResolveCommand()
        : base("resolve")
    {
    }

    public override IEnumerable<string> Arguments
    {
        get
        {
            foreach (var arg in base.Arguments)
                yield return arg;
            yield return "--list";
        }
    }
}

The sample output:

analyzing merge #7 between revisions #5 and #6
analyzing merge #10 between revisions #9 and #8
merge changeset #10 between revisions #9 and #8 had 1 unresolved file(s)
  test1.txt

Zip-file with everything (LINQPad script, Mercurial.Net assembly, go compile it if you don't trust me, and the test repository I executed it against above):

  • here: zip-file

BIG CAVEAT: Run this only in a clone of your repository! I won't be held responsible if this corrupts your repository, however unlikely I think that would be.


Tis is the shell script variant of Lasses answer:

#!/bin/bash

test "$1" = "--destroy-my-working-copy" || exit

hg log -r 'merge()' --template '{parents} {rev}\n' | sed -e 's/[0-9]*://g' | while read p1 p2 commit
do
        echo "-------------------------"
        echo examine $commit
        LC_ALL=C hg up -C -r $p1 >/dev/null
        LC_ALL=C hg merge -r 26 --config ui.merge=internal:merge 2>&1 | grep failed
done

hg up -C -r tip > /dev/null

Example output:

> mergetest.sh --destroy-my-working-copy
-------------------------
examine 7
-------------------------
examine 11
-------------------------
examine 23
-------------------------
examine 31
-------------------------
examine 37
merging test.py failed!
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜