在Mercurial中,修订号是特定于本地克隆的,它们作为一种工具提供,以比变更集ID更加用户友好的方式指向特定的变更集。
然而,当显示时间线图时,TortoiseHG将按修订号订购。如果某个人不经常提交提交,你将立即使用相邻的修订版号提交所有提交,这将使图表的整个点陷入混乱。
这个图表将更具洞察力:有两个开发线,两个作者,一个合并另一个的工作:
因此,是否可以手动重新排序修订号(只要the topological order of changesets仍然与新号码兼容?
感谢。
答案 0 :(得分:5)
我有一个类似的用例:我来到这个问题是因为我一次从不同的分支推送了一堆变更集,并且它们没有按日期排序。我现在与低编号的父母合并,实际上并不是那么古老;滚动日志以查看它们是一种痛苦。我按照@ Ry4an的回答中提出的方法 - 如果你能预测(即计算)你想要的变更集顺序,那真的很简单。
如果您的工作流程仅合并分支头,您可以通过按日期对revsets进行排序来获得所需的修订顺序。您可以使用以下命令执行此操作:
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
我应该注意(在其他人做之前:-))由于计算增量的方式,这将增加存储库的存储大小。我不介意。
答案 1 :(得分:3)
正如其他人所说,这是可能的,但可能不值得工作,因为它将是您的克隆本地(以及克隆的任何克隆)。这不是你可以推送到远程存储库而不是首先删除所有内容的东西,即使你这样做,那些从本地远程存储库中克隆的人在他们拉动时也会看到“没有变化”。
也就是说,如果你想尝试,只需要在一个新的克隆中使用一系列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
拉动时,你会得到那个变化集和它的所有祖先,所以如果你对这些点进行拉动如果匿名分支一次合并一个,那么您将只从该开发线中提取其他变更集。
答案 2 :(得分:1)
可以重新排序您的存储库(这是contrib/shrink-revlog.py
所做的)。但在这种情况下,这似乎有点过分和复杂。
由于这主要是显示问题,因此您应该要求THG实施您想要的重新排序。我承认你不知道你在上图中发现了什么。
答案 3 :(得分:1)
以下是使用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;
}
}
}
答案 4 :(得分:0)
归功于@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:对于Windows提示的反引号,Michael Burr也应该归功于:windows subcommand evaluation