我如何在 Mercurial 中创建一个补丁文件,其中还包含所包含子存储库的更改

发布于 2024-12-12 03:12:08 字数 183 浏览 0 评论 0原文

我们正在与多个人合作开展同一个项目,并使用 Mercurial 作为我们的 DVCS。我们的项目确实有几个子存储库。 我们确实需要通过邮件相互发送补丁,因为目前无法从主存储库中推送和拉取补丁。 导出命令 - 如果在主服务器上执行,只会为主服务器创建补丁,而不是为子存储库创建补丁。我们可以手动为这些创建路径,但我们想知道是否有更简单的方法来做到这一点?

We are working with severall people on the same project and are using Mercurial as our DVCS. Our project does have severall subrepos.
We do need to send patches to each other by mail, because it is at this moment impossible to push and pull from the master repo.
The export command - if executed on the master will only create a patch for the master, and not for the subrepo's. We could manually create pathes for those, but we would like to know if there is an easier way to do this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

瑾夏年华 2024-12-19 03:12:08

正如您所注意到的,对此没有内置支持。

也许您可以使用我编写的 onsub 扩展。这使得为​​子存储库生成补丁变得更加容易:

$ hg onsub 'hg export -r tip -o $HG_REPO/sub-$HG_SUBPATH.diff' 

HG_SUBPATH 变量被替换为子存储库的路径。

如果您有嵌套子存储库,则失败,因为 hg export 无法编写补丁名称 sub-foo/bar/baz.diff。因此,您需要调整路径并将 / 替换为 - 或类似内容。您应该能够使用类似的 hg onsub 调用导入补丁。

如果您成功了,请随时在 wiki 页面上添加有关它的注释。也欢迎您看看是否可以扩展 onsub 扩展以使其更容易,也许可以通过添加一个选项来使 onsub 也在顶级存储库上运行,以及一个可以直接使用而不是咀嚼的新变量>HG_SUBPATH。

There is no built-in support for this, as you've noticed.

Maybe you can use the onsub extension I wrote. That makes it a little easier to generate patches for the subrepos:

$ hg onsub 'hg export -r tip -o $HG_REPO/sub-$HG_SUBPATH.diff' 

The HG_SUBPATH variable is replaced with the path to the subrepo.

That fails if you have nested subrepos since hg export cannot write a patch names sub-foo/bar/baz.diff. So you need to massage the path and replace / with - or similar. You should be able to import the patches with a similar hg onsub call.

If you get it working, then feel free to add a note about it on the wiki page. You're also welcome to see if you can extend the onsub extension to make this easier, perhaps by adding an option to make onsub run on the top-level repo too, and a new variable that can be used directly instead of munging HG_SUBPATH.

旧时浪漫 2024-12-19 03:12:08
  • hg archive 知道子存储库:您可以从单个变更集中导出更改(如果子存储库在提交中受到影响,它们会递归地进行)
  • hg diff 也知道
  • hg commit --subrepos 的情况下的子存储库 提交还必须包括来自子存储库的所有受影响的文件,因此 - 导出也会导出(?)这些文件
  • hg archive knows about subrepo: you can export changes from single changeset (and they walk recursively, if subrepos affected in commit)
  • hg diff also knows about subrepos
  • in case of hg commit --subrepos this commit must include all affected files from subrepos also, thus - export will export (?) these files also
浸婚纱 2024-12-19 03:12:08

正如马丁建议的那样,我编写了自己的扩展。我将在这里发布代码,希望有人能改进它或使其在 Mercurial 中默认可用...

谢谢马丁,我基于你的 onsub 扩展,是的,我知道它有一些问题,但现在,它达到了它的目的。 (超过 10 个子存储库和超过 1 层嵌套的问题)

"""execute the Bundle command in a repository and each subrepository"""

# bundlesb.py - execute the Bundle command in a repository and each subrepository
#
# Copyright 2012 Johan G.
#
# This software may be used and distributed according to the terms of
# the GNU General Public License version 2 or any later version.

import os
import zipfile
from mercurial.i18n import _
from mercurial import extensions, subrepo, util

def bundlesb(ui, repo, *args, **opts):
    """execute the Bundle command in a repository and each subrepository

    Creates a combined bundle (with hgs extention) for the current 
    repository. 

    Because the revision numbers of the root and the subrepos will differ,
    we cannot reliably choose a revision to start from. A date to start
    from should be nice, but i have not taken the time to implement this.
    Instead i choose to take the N (default=10) last changesets into account.
    This seems to work well in our environment. Instead of providing the
    number of changesets for the operation, you can also specify -a to
    include all changesets. 

    Use --verbose/-v to print information and the subrepo
    name for each subrepo. 
    """
    ui.status("Starting operation\n")
    zipname = os.path.splitext(' '.join(args))[0]
    if (zipname==''):
       zipname = os.path.join(repo.root, os.path.split(repo.root)[1])
       #raise ValueError("FILE cannot be empty")
    zipname= zipname + '.hgs'
    allchangesets=opts.get('all')
    changesets=opts.get('changesets')
    ui.debug(_("input filename=%s ; AllChangesets=%s ; Changesets=%s \n") % (zipname, allchangesets, changesets))
    files=[]

    #work on the root repository
    runcmd(ui, repo.root, files, "0Root", repo['.'].rev(), ".", changesets, allchangesets)

    #do the same for each subrepository
    foreach(ui, repo, files, changesets, allchangesets)

    # open the zip file for writing, and write stuff to it
    ui.status("creating file: " + zipname + "\n\n")

    file = zipfile.ZipFile(zipname, "w" )
    for name in files:
        file.write(name, os.path.basename(name), zipfile.ZIP_DEFLATED)
    file.close()

    # open the file again, to see what's in it

    file = zipfile.ZipFile(zipname, "r")
    for info in file.infolist():
        ui.debug(info.filename + " " + str(info.date_time) + " " + str(info.file_size) + " " + str(info.compress_size) +"\n")
        #delete all the compressed .hg files
    os.remove(os.path.join(repo.root, info.filename)) 

    ui.status("\nOperation complete\n")


def foreach(ui, repo, files, changesets, allchangesets):
    ctx = repo['.']
    work = [(1, ctx.sub(subpath)) for subpath in sorted(ctx.substate)]

    while work:
        (depth, sub) = work.pop(0)

        if hasattr(subrepo, 'relpath'):
            relpath = subrepo.relpath(sub)
        else:
            relpath = subrepo.subrelpath(sub)

        rev=sub._repo[sub._state[1]].rev()

        ui.debug(str(rev) + " " + str(sub._repo[sub._state[1]].user()) + " " + str(sub._repo[sub._state[1]].date()) + "\n")

        if depth>1:
            raise Exception("No support for nested levels deeper than 1 yet.")

    runcmd(ui, repo.root, files, str(depth) + relpath, rev, relpath, changesets, allchangesets)

        if isinstance(sub, subrepo.hgsubrepo):
            rev = sub._state[1]
            ctx = sub._repo[rev]
            w = [(depth + 1, ctx.sub(subpath)) 
                 for subpath in sorted(ctx.substate)]
            work.extend(w)

def runcmd(ui, root, files, name, revision, path, changesets, allchangesets):
    files.append(root + "/" + name + ".hg")
    if (revision<=changesets) or allchangesets:
       cmd="hg bundle -a " + root + "/" + name + ".hg"
    else:
       cmd="hg bundle --base " + str(revision-changesets)+ " "  + root + "/" + name + ".hg"
    ui.note(_("Working on '%s' in %s\n") % (path, root))
    ui.debug( "command line: "+ cmd +"\n")
    util.system(cmd, 
        cwd=os.path.join(root, path),
        onerr=util.Abort,
        errprefix=_('terminated bundlesub in %s') % path)


cmdtable = {
    "bundlesb":
        (bundlesb,
         [('c', 'changesets', 10, _('the number of recent changesets to include in the bundle'), 'N'),
          ('a', 'all', None, _('include all changesets in the bundle')),],
         _('[-c|-a] FILE...'))

}

反之亦然:

 """execute the UnBundle command in a repository and each subrepository
           for a file created with BundleSb"""

# unbundlesub.py - execute the UnBundle command in a repository and each subrepository
#
# Copyright 2012 Johan G.
#
# This software may be used and distributed according to the terms of
# the GNU General Public License version 2 or any later version.

import os
import zipfile
#import glob
from mercurial.i18n import _
from mercurial import extensions, subrepo, util

def unbundlesb(ui, repo, *args, **opts):
    """execute the UnBundle command in a repository and each subrepository
        for a file created with BundleSb

    Updates the current repository from a combined bundle (with hgs extention). 

    Use --verbose/-v to print more detailed information during the operation. 
    """
    ui.status("Starting unbundle operation\n")

    update = opts.get('update')
    file = os.path.splitext(' '.join(args))[0] + '.hgs'
    if (file==''):
       raise ValueError("FILE cannot be empty")

    ui.debug("input filename=" + file + "\n")
    zfile = zipfile.ZipFile(file, "r" )
    for info in zfile.infolist():
        ui.debug(info.filename + " " + str(info.date_time) + " " + str(info.file_size) + " " + str(info.compress_size) +"\n")
    zfile.extract(info,repo.root)
        runcmd(ui, repo.root, info.filename, update)
        #delete all the compressed .hg files
    os.remove(os.path.join(repo.root, info.filename)) 

    zfile.close()

    ui.status("\nOperation complete\n")


def runcmd(ui, root, name, update):
    level=name[0]
    rep=name[1:len(name)-3]
    ui.debug(_("Detected level=%s for repository %s \n") % (level, rep))
    cmd="hg unbundle "
    if (update): cmd= cmd + "-u "
    cmd= cmd + root + "\\" + name 
    ui.note(_("Working on '%s' in %s\n") % (rep, root))
    if (level == '1'):
        wd=os.path.join(root, rep)
    elif (level=='0'):
        wd=root
    else:
       raise Exception("Do not know what to do with a level >1")

    ui.debug(_("command line: %s in working directory %s\n") % (cmd, wd))

    util.system(cmd, 
            cwd=wd,
            onerr=util.Abort,
            errprefix=_('terminated unbundlesub in %s') % rep)


cmdtable = {
    "unbundlesb":
        (unbundlesb,
         [('u', 'update', None,
           _('update to new branch head if changesets were unbundled'))],
         _('[-u] FILE...'))
}

As Martin suggested, i wrote my own extension. I will post the code here, in the hope someone will improve it or make it defaulf available in mercurial ...

Thanks Martin, i based it on your onsub extension, and yes i know there are a few problems with it, but for now, it serves its purpose. (problems with more than 10 subrepos and more than 1 level of nesting)

"""execute the Bundle command in a repository and each subrepository"""

# bundlesb.py - execute the Bundle command in a repository and each subrepository
#
# Copyright 2012 Johan G.
#
# This software may be used and distributed according to the terms of
# the GNU General Public License version 2 or any later version.

import os
import zipfile
from mercurial.i18n import _
from mercurial import extensions, subrepo, util

def bundlesb(ui, repo, *args, **opts):
    """execute the Bundle command in a repository and each subrepository

    Creates a combined bundle (with hgs extention) for the current 
    repository. 

    Because the revision numbers of the root and the subrepos will differ,
    we cannot reliably choose a revision to start from. A date to start
    from should be nice, but i have not taken the time to implement this.
    Instead i choose to take the N (default=10) last changesets into account.
    This seems to work well in our environment. Instead of providing the
    number of changesets for the operation, you can also specify -a to
    include all changesets. 

    Use --verbose/-v to print information and the subrepo
    name for each subrepo. 
    """
    ui.status("Starting operation\n")
    zipname = os.path.splitext(' '.join(args))[0]
    if (zipname==''):
       zipname = os.path.join(repo.root, os.path.split(repo.root)[1])
       #raise ValueError("FILE cannot be empty")
    zipname= zipname + '.hgs'
    allchangesets=opts.get('all')
    changesets=opts.get('changesets')
    ui.debug(_("input filename=%s ; AllChangesets=%s ; Changesets=%s \n") % (zipname, allchangesets, changesets))
    files=[]

    #work on the root repository
    runcmd(ui, repo.root, files, "0Root", repo['.'].rev(), ".", changesets, allchangesets)

    #do the same for each subrepository
    foreach(ui, repo, files, changesets, allchangesets)

    # open the zip file for writing, and write stuff to it
    ui.status("creating file: " + zipname + "\n\n")

    file = zipfile.ZipFile(zipname, "w" )
    for name in files:
        file.write(name, os.path.basename(name), zipfile.ZIP_DEFLATED)
    file.close()

    # open the file again, to see what's in it

    file = zipfile.ZipFile(zipname, "r")
    for info in file.infolist():
        ui.debug(info.filename + " " + str(info.date_time) + " " + str(info.file_size) + " " + str(info.compress_size) +"\n")
        #delete all the compressed .hg files
    os.remove(os.path.join(repo.root, info.filename)) 

    ui.status("\nOperation complete\n")


def foreach(ui, repo, files, changesets, allchangesets):
    ctx = repo['.']
    work = [(1, ctx.sub(subpath)) for subpath in sorted(ctx.substate)]

    while work:
        (depth, sub) = work.pop(0)

        if hasattr(subrepo, 'relpath'):
            relpath = subrepo.relpath(sub)
        else:
            relpath = subrepo.subrelpath(sub)

        rev=sub._repo[sub._state[1]].rev()

        ui.debug(str(rev) + " " + str(sub._repo[sub._state[1]].user()) + " " + str(sub._repo[sub._state[1]].date()) + "\n")

        if depth>1:
            raise Exception("No support for nested levels deeper than 1 yet.")

    runcmd(ui, repo.root, files, str(depth) + relpath, rev, relpath, changesets, allchangesets)

        if isinstance(sub, subrepo.hgsubrepo):
            rev = sub._state[1]
            ctx = sub._repo[rev]
            w = [(depth + 1, ctx.sub(subpath)) 
                 for subpath in sorted(ctx.substate)]
            work.extend(w)

def runcmd(ui, root, files, name, revision, path, changesets, allchangesets):
    files.append(root + "/" + name + ".hg")
    if (revision<=changesets) or allchangesets:
       cmd="hg bundle -a " + root + "/" + name + ".hg"
    else:
       cmd="hg bundle --base " + str(revision-changesets)+ " "  + root + "/" + name + ".hg"
    ui.note(_("Working on '%s' in %s\n") % (path, root))
    ui.debug( "command line: "+ cmd +"\n")
    util.system(cmd, 
        cwd=os.path.join(root, path),
        onerr=util.Abort,
        errprefix=_('terminated bundlesub in %s') % path)


cmdtable = {
    "bundlesb":
        (bundlesb,
         [('c', 'changesets', 10, _('the number of recent changesets to include in the bundle'), 'N'),
          ('a', 'all', None, _('include all changesets in the bundle')),],
         _('[-c|-a] FILE...'))

}

And the other way around:

 """execute the UnBundle command in a repository and each subrepository
           for a file created with BundleSb"""

# unbundlesub.py - execute the UnBundle command in a repository and each subrepository
#
# Copyright 2012 Johan G.
#
# This software may be used and distributed according to the terms of
# the GNU General Public License version 2 or any later version.

import os
import zipfile
#import glob
from mercurial.i18n import _
from mercurial import extensions, subrepo, util

def unbundlesb(ui, repo, *args, **opts):
    """execute the UnBundle command in a repository and each subrepository
        for a file created with BundleSb

    Updates the current repository from a combined bundle (with hgs extention). 

    Use --verbose/-v to print more detailed information during the operation. 
    """
    ui.status("Starting unbundle operation\n")

    update = opts.get('update')
    file = os.path.splitext(' '.join(args))[0] + '.hgs'
    if (file==''):
       raise ValueError("FILE cannot be empty")

    ui.debug("input filename=" + file + "\n")
    zfile = zipfile.ZipFile(file, "r" )
    for info in zfile.infolist():
        ui.debug(info.filename + " " + str(info.date_time) + " " + str(info.file_size) + " " + str(info.compress_size) +"\n")
    zfile.extract(info,repo.root)
        runcmd(ui, repo.root, info.filename, update)
        #delete all the compressed .hg files
    os.remove(os.path.join(repo.root, info.filename)) 

    zfile.close()

    ui.status("\nOperation complete\n")


def runcmd(ui, root, name, update):
    level=name[0]
    rep=name[1:len(name)-3]
    ui.debug(_("Detected level=%s for repository %s \n") % (level, rep))
    cmd="hg unbundle "
    if (update): cmd= cmd + "-u "
    cmd= cmd + root + "\\" + name 
    ui.note(_("Working on '%s' in %s\n") % (rep, root))
    if (level == '1'):
        wd=os.path.join(root, rep)
    elif (level=='0'):
        wd=root
    else:
       raise Exception("Do not know what to do with a level >1")

    ui.debug(_("command line: %s in working directory %s\n") % (cmd, wd))

    util.system(cmd, 
            cwd=wd,
            onerr=util.Abort,
            errprefix=_('terminated unbundlesub in %s') % rep)


cmdtable = {
    "unbundlesb":
        (unbundlesb,
         [('u', 'update', None,
           _('update to new branch head if changesets were unbundled'))],
         _('[-u] FILE...'))
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文