8

In Mercurial , revision numbers are local-clone specific, and they are provided as a facility to point to a specific changeset in a more user-friendly way than a changeset id.

However when displaying the timeline graph, TortoiseHG will order by revision number. If some guy pushes commits not that often, you'll get all his commits at once with adjacent revision numbers, and it will mess up the whole point of the graph.

enter image description here

This graph would be more insightful: there are two lines of development, with two authors, one merging the work of the other one:

enter image description here

Therefore, is it possible to manually reorder the revision numbers (as long as the topological order of changesets is still compatible with the new numbers?

Thanks.

Community
  • 1
  • 1
Benoit
  • 76,634
  • 23
  • 210
  • 236
  • 1
    Must admit, I don't see the issue. The graph shows topology. The real world timing of commits is secondary. – Paul S Sep 17 '12 at 11:59
  • 3
    @Paul S: When you have many transversal lines in your graph, they can be hard to follow. Moving commits 404–405 between commits 370—371 and 406 right after 371, 407 right after 380, 408 and 409 right after 399, would produce a graph of lesser width (two columns actually). – Benoit Sep 17 '12 at 13:21
  • @Paul S: see edit. Can't you see that one is really more clear, and has the exact same topology? – Benoit Sep 17 '12 at 13:30
  • Ah yes, I see what you mean. I think that graphlog seem to be quite good at this, but tortoise isn't, which is a little odd. – Paul S Sep 17 '12 at 17:32
  • 1
    Ok, so I'm 3 years late here, but can't you just ask your co-worker to use `hg rebase` instead of `hg merge`? – Jacklynn Jun 09 '15 at 19:09
  • I had the same question. It came as an unpleasant surprise to realise that TortoiseHg wasn't actually doing it in chronological order; it just looks like that if you're pulling and pushing frequently, or working by yourself. – O'Rooney Jun 21 '16 at 03:41

5 Answers5

6

I have a similar use case: I came to this question because I pushed a bunch of changesets from different branches at once, and they did not arrive sorted by date. I now have some merges with low-numbered parents that are not in fact that old; scrolling up and down the log to see them is a pain. I followed the approach suggested in @Ry4an's answer-- it's really quite simple, if you can predict (i.e., compute) the changeset order you want.

If your workflow only merges branch heads, you can get the desired revision order by sorting revsets by date. This you can do with the following command:

hg log -r 'sort(0:tip, date)' --template '{rev}\n'

You can then clone the repository and use a loop in your favorite scripting language to pull the revisions one by one in chronological order. Rather than init a repository from scratch, I ended up doing it like this:

hg clone -r 0 messy-repo sorted-repo
cd sorted-repo
for REV in `hg log -R ../messy-repo -r 'sort(1:tip, date)' --template '{rev}\n'`
do 
    hg pull ../messy-repo -r $REV
done

I should note (before somebody else does :-)) that this will increase the storage size of the repository, because of the way deltas are computed. I don't mind.

alexis
  • 48,685
  • 16
  • 101
  • 161
  • This is almost perfect, except that it always puts revision 0 at the start of the new repository, so if even that is out of order then that won't be fixed. To solve that, use `hg init` instead of `hg clone ...`, then change `...sort(1:tip, date)...` to `...sort(:, date)...`. – Arthur Tacca Nov 09 '16 at 10:37
4

As others are saying, it's possible, but probably not worth the work, since it would be local to your clone (and any clones of your clone). It's not something you could push to a remote repository w/o deleting everything there first, and even if you did that the people who had clones from that remote repository locally would see "no changes" when they pulled.

That said, if you want to try you just need to use a series of hg pull -r REV commands into a new clone. Something like this:

hg init my_reordered_clone
cd my_reordered_clone
hg pull -r d84b1 ../un_reordered_clone
hg pull -r 6d269 ../un_reordered_clone
hg pull -r bb9e4 ../un_reordered_clone

Clearly that's too much work to be worth it for aesthetic purposes, but the concept there is that when you pull with -r you get that changeset and all of its ancestors, so if you do your pulls at the points where anonymous branches merged one at a time, you'll be pulling only the additional changesets from that line of development.

Ry4an Brase
  • 78,112
  • 7
  • 148
  • 169
  • 1
    I wouldn't say it's too much work: If you know the order you want, it's just a simple loop in your favorite scripting language. I've a similar use case (see my answer). – alexis Apr 15 '13 at 09:51
1

It is possible to reorder your repository (that's what contrib/shrink-revlog.py does). But in this case it seems overkill and complicated.

Since this is mostly a display issue, you should instead ask THG to implement the reordering you would like to have. I admittedly have no idea what you find messed up in the graph above.

tonfa
  • 24,151
  • 2
  • 35
  • 41
  • 2
    It is already down as a proposal on the THG issue tracker [here](https://bitbucket.org/tortoisehg/thg/issue/580/), but it was raised in April 2011 so I guess that the THG team don't have it as a high priority either. – Steve Kaye Sep 17 '12 at 10:52
1

Below is an implementation to narrow the graph width using a csscript

The command to copy your repo with the history reordered is

cs-script\cscs.exe HgSortMergeChangesets.cs fromRepo toNewRepo

The file "HgSortMergeChangesets.cs" has the following contents:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;

class Program
{
    static int Main(string[] args)
    {
        if (args.Length != 2)
        {
            Console.WriteLine("usage: SortHgRepo <fromRepo> <toRepo>");
            return -1;
        }

        var fromRepo = new DirectoryInfo(args[0]);
        var toRepo = new DirectoryInfo(args[1]);


        int errorCode = VerifyParameters(toRepo, fromRepo);
        if (errorCode != 0)
        {
            return errorCode;
        }

        var revsOutput = ExecCmdReturnStdOut("hg.exe", "log -r \"sort(merge(),date)\" -T \"{rev} {date|date}\\n\"", fromRepo.FullName, Console.WriteLine);

        var mergeChangesets = ParseChangesetLog(revsOutput) 
            .ToList();

        ExecCmdReturnStdOut("hg.exe", string.Format("clone -U -r 0 . \"{0}\"", toRepo.FullName), fromRepo.FullName, Console.WriteLine);

        foreach (var changeset in mergeChangesets)
        {
            ExecCmdReturnStdOut("hg.exe", string.Format("pull \"{1}\" -r {0}", changeset.ChangesetId, fromRepo.FullName), toRepo.FullName, Console.WriteLine);
        }

        ExecCmdReturnStdOut("hg.exe", string.Format("pull \"{0}\"", fromRepo.FullName), toRepo.FullName, Console.WriteLine);

        return 0;

    }

    private static int VerifyParameters(DirectoryInfo toRepo, DirectoryInfo fromRepo)
    {
        if (toRepo.Exists)
        {
            Console.WriteLine("The destination repo already exists: {0}", toRepo);
            {
                return -2;
            }
        }


        if (!fromRepo.Exists)
        {
            Console.WriteLine("The source repo does not exists: {0}", fromRepo);
            {
                return -3;
            }
        }

        // make sure the source dir is a repo
        try
        {
            var identity = ExecCmdReturnStdOut("hg.exe", "identify", fromRepo.FullName, Console.WriteLine);
            Console.WriteLine(identity);
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
            Console.WriteLine("The source directory, {0}, does not look like an Hg repo.", fromRepo);
            return -4;
        }
        return 0;
    }


    private static IEnumerable<Changeset> ParseChangesetLog(string revsOutput)
    {
        using (var r = new StringReader(revsOutput))
        {
            string line;
            while ((line = r.ReadLine()) != null)
            {
                var spacePos = line.IndexOf(' ');
                yield return new Changeset
                {
                    ChangesetId = int.Parse(line.Substring(0, spacePos)),
                    DateStr = line.Substring(spacePos + 1)
                };
            }
        }
    }

    class Changeset
    {
        public int ChangesetId;
        public string DateStr;
        public DateTime Date { get { return DateTime.ParseExact(DateStr, "ddd MMM dd H:mm:ss yyyy zzz", null); } }
    }



    public static string ExecCmdReturnStdOut(string program, string args, string workingDir, Action<string> writeline)
    {

        writeline(String.Format("Executing: \"{0}\" {1} in {2}", program, args, workingDir));

        using (var proc = new Process())
        {

            proc.StartInfo.Arguments = args;
            proc.StartInfo.CreateNoWindow = false;
            proc.StartInfo.FileName = program;
            proc.StartInfo.WorkingDirectory = workingDir;
            proc.StartInfo.RedirectStandardError = false;
            proc.StartInfo.RedirectStandardOutput = true;
            proc.StartInfo.UseShellExecute = false;

            proc.Start();

            var output = proc.StandardOutput.ReadToEnd();

            proc.WaitForExit();

            if (proc.ExitCode != 0)
            {
                throw new Exception(string.Format("error code {0} returned when running command {1} in dir {2}", proc.ExitCode, "\"" + program + "\" " + args, workingDir));
            }

            return output;
        }
    }


}
Clay Lenhart
  • 1,597
  • 15
  • 17
0

due credit to @alexis and @Ry4an

I know this workaround of making a new clone ordered by date, and it works, although it might be slow for large repositories. It depends on whether you consider it worth the trouble.

Today, I was pulling to a Windows PC and this caught me, so I wanted to quickly automate it. So here is a rewrite for Windows command prompt:

hg clone -r 0 messy-repo sorted-repo
cd sorted-repo
for /f "usebackq tokens=*" %r in (`hg log -R ../messy-repo -r "sort(1:tip, date)" --template "{rev}\n"`) do @echo %r && @hg pull ../messy-repo -r %r

Notice: percent signs should be doubled if this goes within a batch script file, otherwise single percent for directly input it at the command prompt.

PD: also due credit to Michael Burr for the backquote for Windows prompt: windows subcommand evaluation

Community
  • 1
  • 1
arhak
  • 2,488
  • 1
  • 24
  • 38