在 Mercurial 中,修订号是特定于本地克隆的,它们作为一种工具提供,以比变更集 ID 更用户友好的方式指向特定变更集。
然而,当显示时间线图时,TortoiseHG 将按修订号排序。如果某个人不经常推送提交,您将立即获得他的所有提交以及相邻的修订号,这会弄乱图表的整个要点。
这张图会更有洞察力:有两条发展路线,有两位作者,一位合并了另一位的工作:
因此,是否可以手动重新排序修订号(只要变更集的拓扑顺序仍然与新编号兼容?
谢谢。
在 Mercurial 中,修订号是特定于本地克隆的,它们作为一种工具提供,以比变更集 ID 更用户友好的方式指向特定变更集。
然而,当显示时间线图时,TortoiseHG 将按修订号排序。如果某个人不经常推送提交,您将立即获得他的所有提交以及相邻的修订号,这会弄乱图表的整个要点。
这张图会更有洞察力:有两条发展路线,有两位作者,一位合并了另一位的工作:
因此,是否可以手动重新排序修订号(只要变更集的拓扑顺序仍然与新编号兼容?
谢谢。
我有一个类似的用例:我提出这个问题是因为我一次从不同的分支推送了一堆变更集,并且它们没有按日期排序。我现在与实际上并不老的少数父母合并;上下滚动日志以查看它们是一种痛苦。我遵循了@Ry4an 的回答中建议的方法——如果你可以预测(即计算)你想要的变更集顺序,这真的很简单。
如果您的工作流程仅合并分支头,您可以通过按日期对修订集进行排序来获得所需的修订顺序。您可以使用以下命令执行此操作:
hg log -r 'sort(0:tip, date)' --template '{rev}\n'
然后,您可以克隆存储库并使用您喜欢的脚本语言中的循环按时间顺序逐一提取修订。我没有从头开始初始化存储库,而是这样做了:
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
我应该注意(在其他人这样做之前:-))这将增加存储库的存储大小,因为计算增量的方式。我不介意。
正如其他人所说,这是可能的,但可能不值得这项工作,因为它将是您的克隆(以及您的克隆的任何克隆)的本地。这不是您可以在不先删除那里的所有内容的情况下推送到远程存储库的东西,即使您这样做了,从本地远程存储库克隆的人在拉取时也会看到“没有更改”。
hg pull -r REV
也就是说,如果你想尝试,你只需要在一个新的克隆中使用一系列命令。像这样的东西:
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
显然,出于美学目的,这工作量太大,不值得,但是其中的概念是,当您拉动时,-r
您会得到该变更集及其所有祖先,因此,如果您在匿名分支一次合并一个的点上进行拉动,您将只从该开发线中提取额外的变更集。
可以重新排序您的存储库(就是这样contrib/shrink-revlog.py
做的)。但在这种情况下,它似乎有点矫枉过正和复杂。
由于这主要是一个显示问题,因此您应该要求 THG 实施您想要的重新排序。诚然,我不知道您在上图中发现了什么问题。
下面是使用csscript缩小图形宽度的实现
使用重新排序的历史复制您的存储库的命令是
cs-script\cscs.exe HgSortMergeChangesets.cs fromRepo toNewRepo
文件“HgSortMergeChangesets.cs”有以下内容:
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;
}
}
}
归功于@alexis 和@Ry4an
我知道这种按日期排序的新克隆的解决方法,它可以工作,尽管对于大型存储库可能会很慢。这取决于您是否认为值得麻烦。
今天,我正在使用 Windows PC,这引起了我的注意,所以我想快速实现它的自动化。所以这里是对 Windows 命令提示符的重写:
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
注意:如果在批处理脚本文件中,百分号应该加倍,否则在命令提示符下直接输入百分号。
PD:还应归功于 Michael Burr 对 Windows 提示的反引号:windows 子命令评估