* [PATCH 2/3] remote-bzr: add support for pushing
From: Felipe Contreras @ 2012-11-04 2:22 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, Johannes Schindelin, Felipe Contreras
In-Reply-To: <1351995723-20396-1-git-send-email-felipe.contreras@gmail.com>
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
contrib/remote-helpers/git-remote-bzr | 34 ++++++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)
diff --git a/contrib/remote-helpers/git-remote-bzr b/contrib/remote-helpers/git-remote-bzr
index 76a609a..de37217 100755
--- a/contrib/remote-helpers/git-remote-bzr
+++ b/contrib/remote-helpers/git-remote-bzr
@@ -26,6 +26,9 @@ bzrlib.plugin.load_plugins()
import bzrlib.revisionspec
from bzrlib.plugins.fastimport import exporter as bzr_exporter
+from bzrlib.plugins.fastimport.processors import generic_processor as bzr_generic_processor
+
+import fastimport.parser
import sys
import os
@@ -140,9 +143,38 @@ def do_import(parser):
sys.stdout.flush()
+def do_export(parser):
+ global dirname
+
+ parser.next() # can't handle 'done'?
+
+ controldir = parser.repo.bzrdir
+
+ path = os.path.join(dirname, 'marks-bzr')
+ params = { 'import-marks': path, 'export-marks': path }
+
+ proc = bzr_generic_processor.GenericProcessor(controldir, params)
+ p = fastimport.parser.ImportParser(sys.stdin)
+ proc.process(p.iter_commands)
+
+ if parser.repo.last_revision() != marks.get_tip('master'):
+ print 'ok %s' % 'refs/heads/master'
+
+ print
+
def do_capabilities(parser):
+ global dirname
+
print "import"
+ print "export"
print "refspec refs/heads/*:%s/heads/*" % prefix
+
+ path = os.path.join(dirname, 'marks-git')
+
+ if os.path.exists(path):
+ print "*import-marks %s" % path
+ print "*export-marks %s" % path
+
print
def do_list(parser):
@@ -178,6 +210,8 @@ def main(args):
do_list(parser)
elif parser.check('import'):
do_import(parser)
+ elif parser.check('export'):
+ do_export(parser)
else:
die('unhandled command: %s' % line)
sys.stdout.flush()
--
1.8.0
^ permalink raw reply related
* [PATCH 3/3] remote-bzr: add simple tests
From: Felipe Contreras @ 2012-11-04 2:22 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, Johannes Schindelin, Felipe Contreras
In-Reply-To: <1351995723-20396-1-git-send-email-felipe.contreras@gmail.com>
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
contrib/remote-helpers/test-bzr.sh | 111 +++++++++++++++++++++++++++++++++++++
1 file changed, 111 insertions(+)
create mode 100755 contrib/remote-helpers/test-bzr.sh
diff --git a/contrib/remote-helpers/test-bzr.sh b/contrib/remote-helpers/test-bzr.sh
new file mode 100755
index 0000000..8594ffc
--- /dev/null
+++ b/contrib/remote-helpers/test-bzr.sh
@@ -0,0 +1,111 @@
+#!/bin/sh
+#
+# Copyright (c) 2012 Felipe Contreras
+#
+
+test_description='Test remote-bzr'
+
+. ./test-lib.sh
+
+if ! test_have_prereq PYTHON; then
+ skip_all='skipping remote-bzr tests; python not available'
+ test_done
+fi
+
+if ! "$PYTHON_PATH" -c 'import bzrlib'; then
+ skip_all='skipping remote-bzr tests; bzr not available'
+ test_done
+fi
+
+cmd=<<EOF
+import bzrlib
+bzrlib.initialize()
+import bzrlib.plugin
+bzrlib.plugin.load_plugins()
+import bzrlib.plugins.fastimport
+EOF
+
+if ! "$PYTHON_PATH" -c "$cmd"; then
+ echo "consider setting BZR_PLUGIN_PATH=$HOME/.bazaar/plugins" 1>&2
+ skip_all='skipping remote-bzr tests; bzr-fastimport not available'
+ test_done
+fi
+
+check () {
+ (cd $1 &&
+ git log --format='%s' -1 &&
+ git symbolic-ref HEAD) > actual &&
+ (echo $2 &&
+ echo "refs/heads/$3") > expected &&
+ test_cmp expected actual
+}
+
+bzr whoami "A U Thor <author@example.com>"
+
+test_expect_success 'cloning' '
+ (bzr init bzrrepo &&
+ cd bzrrepo &&
+ echo one > content &&
+ bzr add content &&
+ bzr commit -m one
+ ) &&
+
+ git clone "bzr::$PWD/bzrrepo" gitrepo &&
+ check gitrepo one master
+'
+
+test_expect_success 'pulling' '
+ (cd bzrrepo &&
+ echo two > content &&
+ bzr commit -m two
+ ) &&
+
+ (cd gitrepo && git pull) &&
+
+ check gitrepo two master
+'
+
+test_expect_success 'pushing' '
+ (cd gitrepo &&
+ echo three > content &&
+ git commit -a -m three &&
+ git push
+ ) &&
+
+ echo three > expected &&
+ cat bzrrepo/content > actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'roundtrip' '
+ (cd gitrepo &&
+ git pull &&
+ git log --format="%s" -1 origin/master > actual) &&
+ echo three > expected &&
+ test_cmp expected actual &&
+
+ (cd gitrepo && git push && git pull) &&
+
+ (cd bzrrepo &&
+ echo four > content &&
+ bzr commit -m four
+ ) &&
+
+ (cd gitrepo && git pull && git push) &&
+
+ check gitrepo four master &&
+
+ (cd gitrepo &&
+ echo five > content &&
+ git commit -a -m five &&
+ git push && git pull
+ ) &&
+
+ (cd bzrrepo && bzr revert) &&
+
+ echo five > expected &&
+ cat bzrrepo/content > actual &&
+ test_cmp expected actual
+'
+
+test_done
--
1.8.0
^ permalink raw reply related
* [PATCH 1/3] Add new remote-bzr transport helper
From: Felipe Contreras @ 2012-11-04 2:22 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, Johannes Schindelin, Felipe Contreras
In-Reply-To: <1351995723-20396-1-git-send-email-felipe.contreras@gmail.com>
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
contrib/remote-helpers/git-remote-bzr | 187 ++++++++++++++++++++++++++++++++++
1 file changed, 187 insertions(+)
create mode 100755 contrib/remote-helpers/git-remote-bzr
diff --git a/contrib/remote-helpers/git-remote-bzr b/contrib/remote-helpers/git-remote-bzr
new file mode 100755
index 0000000..76a609a
--- /dev/null
+++ b/contrib/remote-helpers/git-remote-bzr
@@ -0,0 +1,187 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2012 Felipe Contreras
+#
+
+# Just copy to your ~/bin, or anywhere in your $PATH.
+# Then you can clone with:
+# git clone bzr::/path/to/mercurial/repo/
+#
+# You need to have bzr-fastimport installed:
+# http://wiki.bazaar.canonical.com/BzrFastImport
+#
+# You might also need to find this line bzr-fastimport's
+# code, and modify it:
+#
+# self._use_known_graph = False
+
+import sys
+
+import bzrlib
+bzrlib.initialize()
+
+import bzrlib.plugin
+bzrlib.plugin.load_plugins()
+
+import bzrlib.revisionspec
+
+from bzrlib.plugins.fastimport import exporter as bzr_exporter
+
+import sys
+import os
+import json
+
+def die(msg, *args):
+ sys.stderr.write('ERROR: %s\n' % (msg % args))
+ sys.exit(1)
+
+def warn(msg, *args):
+ sys.stderr.write('WARNING: %s\n' % (msg % args))
+
+class Marks:
+
+ def __init__(self, path):
+ self.path = path
+ self.tips = {}
+ self.load()
+
+ def load(self):
+ if not os.path.exists(self.path):
+ return
+
+ tmp = json.load(open(self.path))
+ self.tips = tmp['tips']
+
+ def dict(self):
+ return { 'tips': self.tips }
+
+ def store(self):
+ json.dump(self.dict(), open(self.path, 'w'))
+
+ def __str__(self):
+ return str(self.dict())
+
+ def get_tip(self, branch):
+ return self.tips.get(branch, '1')
+
+ def set_tip(self, branch, tip):
+ self.tips[branch] = tip
+
+class Parser:
+
+ def __init__(self, repo):
+ self.repo = repo
+ self.line = self.get_line()
+
+ def get_line(self):
+ return sys.stdin.readline().strip()
+
+ def __getitem__(self, i):
+ return self.line.split()[i]
+
+ def check(self, word):
+ return self.line.startswith(word)
+
+ def each_block(self, separator):
+ while self.line != separator:
+ yield self.line
+ self.line = self.get_line()
+
+ def __iter__(self):
+ return self.each_block('')
+
+ def next(self):
+ self.line = self.get_line()
+ if self.line == 'done':
+ self.line = None
+
+def export_branch(branch, name):
+ global prefix, dirname
+
+ marks_path = os.path.join(dirname, 'marks-bzr')
+ ref = '%s/heads/%s' % (prefix, name)
+ tip = marks.get_tip(name)
+ start = "before:%s" % tip
+ rev1 = bzrlib.revisionspec.RevisionSpec.from_string(start)
+ rev2 = bzrlib.revisionspec.RevisionSpec.from_string(None)
+
+ exporter = bzr_exporter.BzrFastExporter(branch,
+ outf=sys.stdout, ref=ref,
+ checkpoint=None,
+ import_marks_file=marks_path,
+ export_marks_file=marks_path,
+ revision=[rev1, rev2],
+ verbose=None, plain_format=True,
+ rewrite_tags=False)
+ exporter.run()
+
+ marks.set_tip(name, branch.last_revision())
+
+def do_import(parser):
+ global dirname
+
+ branch = parser.repo
+ path = os.path.join(dirname, 'marks-git')
+
+ print "feature done"
+ if os.path.exists(path):
+ print "feature import-marks=%s" % path
+ print "feature export-marks=%s" % path
+ sys.stdout.flush()
+
+ while parser.check('import'):
+ ref = parser[1]
+ if ref.startswith('refs/heads/'):
+ name = ref[len('refs/heads/'):]
+ export_branch(branch, name)
+ parser.next()
+
+ print 'done'
+
+ sys.stdout.flush()
+
+def do_capabilities(parser):
+ print "import"
+ print "refspec refs/heads/*:%s/heads/*" % prefix
+ print
+
+def do_list(parser):
+ print "? refs/heads/%s" % 'master'
+ print "@refs/heads/%s HEAD" % 'master'
+ print
+
+def main(args):
+ global marks, prefix, dirname
+
+ alias = args[1]
+ url = args[2]
+
+ d = bzrlib.controldir.ControlDir.open(url)
+ repo = d.open_branch()
+
+ prefix = 'refs/bzr/%s' % alias
+
+ gitdir = os.environ['GIT_DIR']
+ dirname = os.path.join(gitdir, 'bzr', alias)
+
+ if not os.path.exists(dirname):
+ os.makedirs(dirname)
+
+ marks_path = os.path.join(dirname, 'marks-int')
+ marks = Marks(marks_path)
+
+ parser = Parser(repo)
+ for line in parser:
+ if parser.check('capabilities'):
+ do_capabilities(parser)
+ elif parser.check('list'):
+ do_list(parser)
+ elif parser.check('import'):
+ do_import(parser)
+ else:
+ die('unhandled command: %s' % line)
+ sys.stdout.flush()
+
+ marks.store()
+
+sys.exit(main(sys.argv))
--
1.8.0
^ permalink raw reply related
* [PATCH 0/3] New remote-bzr remote helper
From: Felipe Contreras @ 2012-11-04 2:22 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, Johannes Schindelin, Felipe Contreras
Hi,
This is a proof-of-concept remote helper for bzr, that turns out to work rather well.
It uses bzr-fastimport[1], which is what most current bzr<->git tools uses, but
the quality is not that great. After applying an important fix[2], it works
nicely, but you will get tons of verbose messages.
Eventually we might want to implement the same functionality as bzr-fastimport,
but for now this already works nicely enough.
Cheers.
[1] http://wiki.bazaar.canonical.com/BzrFastImport
[2] https://launchpadlibrarian.net/121967844/bzr-fastimport-no-graph.patch
Felipe Contreras (3):
Add new remote-bzr transport helper
remote-bzr: add support for pushing
remote-bzr: add simple tests
contrib/remote-helpers/git-remote-bzr | 221 ++++++++++++++++++++++++++++++++++
contrib/remote-helpers/test-bzr.sh | 111 +++++++++++++++++
2 files changed, 332 insertions(+)
create mode 100755 contrib/remote-helpers/git-remote-bzr
create mode 100755 contrib/remote-helpers/test-bzr.sh
--
1.8.0
^ permalink raw reply
* [PATCH v6 16/16] remote-hg: the author email can be null
From: Felipe Contreras @ 2012-11-04 2:13 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Jeff King, Sverre Rabbelier, Johannes Schindelin,
Ilari Liusvaara, Daniel Barkalow, Michael J Gruber,
Felipe Contreras
In-Reply-To: <1351995218-19889-1-git-send-email-felipe.contreras@gmail.com>
Like 'Foo <>'.
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
contrib/remote-helpers/git-remote-hg | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg
index a9ae844..7929eec 100755
--- a/contrib/remote-helpers/git-remote-hg
+++ b/contrib/remote-helpers/git-remote-hg
@@ -35,9 +35,9 @@ import urllib
#
NAME_RE = re.compile('^([^<>]+)')
-AUTHOR_RE = re.compile('^([^<>]+?)? ?<([^<>]+)>$')
-AUTHOR_HG_RE = re.compile('^(.*?) ?<(.+?)(?:>(.+)?)?$')
-RAW_AUTHOR_RE = re.compile('^(\w+) (?:(.+)? )?<(.+)> (\d+) ([+-]\d+)')
+AUTHOR_RE = re.compile('^([^<>]+?)? ?<([^<>]*)>$')
+AUTHOR_HG_RE = re.compile('^(.*?) ?<(.*?)(?:>(.+)?)?$')
+RAW_AUTHOR_RE = re.compile('^(\w+) (?:(.+)? )?<(.*)> (\d+) ([+-]\d+)')
def die(msg, *args):
sys.stderr.write('ERROR: %s\n' % (msg % args))
--
1.8.0
^ permalink raw reply related
* [PATCH v6 15/16] remote-hg: add option to not track branches
From: Felipe Contreras @ 2012-11-04 2:13 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Jeff King, Sverre Rabbelier, Johannes Schindelin,
Ilari Liusvaara, Daniel Barkalow, Michael J Gruber,
Felipe Contreras
In-Reply-To: <1351995218-19889-1-git-send-email-felipe.contreras@gmail.com>
Some people prefer it this way.
% git config --global remote-hg.track-branches false
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
contrib/remote-helpers/git-remote-hg | 22 ++++++++++++++--------
1 file changed, 14 insertions(+), 8 deletions(-)
diff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg
index dbe309a..a9ae844 100755
--- a/contrib/remote-helpers/git-remote-hg
+++ b/contrib/remote-helpers/git-remote-hg
@@ -449,14 +449,9 @@ def list_head(repo, cur):
g_head = (head, node)
def do_list(parser):
- global branches, bmarks, mode
+ global branches, bmarks, mode, track_branches
repo = parser.repo
- for branch in repo.branchmap():
- heads = repo.branchheads(branch)
- if len(heads):
- branches[branch] = heads
-
for bmark, node in bookmarks.listbookmarks(repo).iteritems():
bmarks[bmark] = repo[node]
@@ -464,7 +459,12 @@ def do_list(parser):
list_head(repo, cur)
- if mode != 'hg':
+ if track_branches:
+ for branch in repo.branchmap():
+ heads = repo.branchheads(branch)
+ if len(heads):
+ branches[branch] = heads
+
for branch in branches:
print "? refs/heads/branches/%s" % branch
@@ -713,16 +713,22 @@ def main(args):
global prefix, dirname, branches, bmarks
global marks, blob_marks, parsed_refs
global peer, mode, bad_mail, bad_name
+ global track_branches
alias = args[1]
url = args[2]
peer = None
- cmd = ['git', 'config', '--get', 'remote-hg.hg-git-compat']
hg_git_compat = False
+ track_branches = True
try:
+ cmd = ['git', 'config', '--get', 'remote-hg.hg-git-compat']
if subprocess.check_output(cmd) == 'true\n':
hg_git_compat = True
+ track_branches = False
+ cmd = ['git', 'config', '--get', 'remote-hg.track-branches']
+ if subprocess.check_output(cmd) == 'false\n':
+ track_branches = False
except subprocess.CalledProcessError:
pass
--
1.8.0
^ permalink raw reply related
* [PATCH v6 14/16] remote-hg: add extra author test
From: Felipe Contreras @ 2012-11-04 2:13 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Jeff King, Sverre Rabbelier, Johannes Schindelin,
Ilari Liusvaara, Daniel Barkalow, Michael J Gruber,
Felipe Contreras
In-Reply-To: <1351995218-19889-1-git-send-email-felipe.contreras@gmail.com>
For hg.hg.
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
contrib/remote-helpers/test-hg-hg-git.sh | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/contrib/remote-helpers/test-hg-hg-git.sh b/contrib/remote-helpers/test-hg-hg-git.sh
index e07bba5..3e76d9f 100755
--- a/contrib/remote-helpers/test-hg-hg-git.sh
+++ b/contrib/remote-helpers/test-hg-hg-git.sh
@@ -370,7 +370,11 @@ test_expect_success 'hg author' '
echo theta > theta &&
hg add theta &&
- hg commit -u "test >test@example.com>" -m "add theta"
+ hg commit -u "test >test@example.com>" -m "add theta" &&
+
+ echo iota > iota &&
+ hg add iota &&
+ hg commit -u "test <test <at> example <dot> com>" -m "add iota"
) &&
hg_push_$x hgrepo-$x gitrepo-$x &&
--
1.8.0
^ permalink raw reply related
* [PATCH v6 13/16] remote-hg: add tests to compare with hg-git
From: Felipe Contreras @ 2012-11-04 2:13 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Jeff King, Sverre Rabbelier, Johannes Schindelin,
Ilari Liusvaara, Daniel Barkalow, Michael J Gruber,
Felipe Contreras
In-Reply-To: <1351995218-19889-1-git-send-email-felipe.contreras@gmail.com>
The base commands come from the tests of the hg-git project.
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
contrib/remote-helpers/test-hg-hg-git.sh | 462 +++++++++++++++++++++++++++++++
1 file changed, 462 insertions(+)
create mode 100755 contrib/remote-helpers/test-hg-hg-git.sh
diff --git a/contrib/remote-helpers/test-hg-hg-git.sh b/contrib/remote-helpers/test-hg-hg-git.sh
new file mode 100755
index 0000000..e07bba5
--- /dev/null
+++ b/contrib/remote-helpers/test-hg-hg-git.sh
@@ -0,0 +1,462 @@
+#!/bin/sh
+#
+# Copyright (c) 2012 Felipe Contreras
+#
+# Base commands from hg-git tests:
+# https://bitbucket.org/durin42/hg-git/src
+#
+
+test_description='Test remote-hg output compared to hg-git'
+
+. ./test-lib.sh
+
+if ! test_have_prereq PYTHON; then
+ skip_all='skipping remote-hg tests; python not available'
+ test_done
+fi
+
+if ! "$PYTHON_PATH" -c 'import mercurial'; then
+ skip_all='skipping remote-hg tests; mercurial not available'
+ test_done
+fi
+
+if ! "$PYTHON_PATH" -c 'import hggit'; then
+ skip_all='skipping remote-hg tests; hg-git not available'
+ test_done
+fi
+
+# clone to a git repo with git
+git_clone_git () {
+ hg -R $1 bookmark -f -r tip master &&
+ git clone -q "hg::$PWD/$1" $2
+}
+
+# clone to an hg repo with git
+hg_clone_git () {
+ (
+ hg init $2 &&
+ cd $1 &&
+ git push -q "hg::$PWD/../$2" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*'
+ ) &&
+
+ (cd $2 && hg -q update)
+}
+
+# clone to a git repo with hg
+git_clone_hg () {
+ (
+ git init -q $2 &&
+ cd $1 &&
+ hg bookmark -f -r tip master &&
+ hg -q push -r master ../$2 || true
+ )
+}
+
+# clone to an hg repo with hg
+hg_clone_hg () {
+ hg -q clone $1 $2
+}
+
+# push an hg repo with git
+hg_push_git () {
+ (
+ cd $2
+ old=$(git symbolic-ref --short HEAD)
+ git checkout -q -b tmp &&
+ git fetch -q "hg::$PWD/../$1" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*' &&
+ git checkout -q $old &&
+ git branch -q -D tmp 2> /dev/null || true
+ )
+}
+
+# push an hg git repo with hg
+hg_push_hg () {
+ (
+ cd $1 &&
+ hg -q push ../$2 || true
+ )
+}
+
+hg_log () {
+ hg -R $1 log --graph --debug | grep -v 'tag: *default/'
+}
+
+git_log () {
+ git --git-dir=$1/.git fast-export --branches
+}
+
+setup () {
+ (
+ echo "[ui]"
+ echo "username = A U Thor <author@example.com>"
+ echo "[defaults]"
+ echo "backout = -d \"0 0\""
+ echo "commit = -d \"0 0\""
+ echo "debugrawcommit = -d \"0 0\""
+ echo "tag = -d \"0 0\""
+ echo "[extensions]"
+ echo "hgext.bookmarks ="
+ echo "hggit ="
+ ) >> "$HOME"/.hgrc &&
+ git config --global receive.denycurrentbranch warn
+ git config --global remote-hg.hg-git-compat true
+
+ export HGEDITOR=/usr/bin/true
+
+ export GIT_AUTHOR_DATE="2007-01-01 00:00:00 +0230"
+ export GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
+}
+
+setup
+
+test_expect_success 'merge conflict 1' '
+ mkdir -p tmp && cd tmp &&
+ test_when_finished "cd .. && rm -rf tmp" &&
+
+ (
+ hg init hgrepo1 &&
+ cd hgrepo1 &&
+ echo A > afile &&
+ hg add afile &&
+ hg ci -m "origin" &&
+
+ echo B > afile &&
+ hg ci -m "A->B" &&
+
+ hg up -r0 &&
+ echo C > afile &&
+ hg ci -m "A->C" &&
+
+ hg merge -r1 || true &&
+ echo C > afile &&
+ hg resolve -m afile &&
+ hg ci -m "merge to C"
+ ) &&
+
+ for x in hg git; do
+ git_clone_$x hgrepo1 gitrepo-$x &&
+ hg_clone_$x gitrepo-$x hgrepo2-$x &&
+ hg_log hgrepo2-$x > hg-log-$x &&
+ git_log gitrepo-$x > git-log-$x
+ done &&
+
+ test_cmp hg-log-hg hg-log-git &&
+ test_cmp git-log-hg git-log-git
+'
+
+test_expect_success 'merge conflict 2' '
+ mkdir -p tmp && cd tmp &&
+ test_when_finished "cd .. && rm -rf tmp" &&
+
+ (
+ hg init hgrepo1 &&
+ cd hgrepo1 &&
+ echo A > afile &&
+ hg add afile &&
+ hg ci -m "origin" &&
+
+ echo B > afile &&
+ hg ci -m "A->B" &&
+
+ hg up -r0 &&
+ echo C > afile &&
+ hg ci -m "A->C" &&
+
+ hg merge -r1 || true &&
+ echo B > afile &&
+ hg resolve -m afile &&
+ hg ci -m "merge to B"
+ ) &&
+
+ for x in hg git; do
+ git_clone_$x hgrepo1 gitrepo-$x &&
+ hg_clone_$x gitrepo-$x hgrepo2-$x &&
+ hg_log hgrepo2-$x > hg-log-$x &&
+ git_log gitrepo-$x > git-log-$x
+ done &&
+
+ test_cmp hg-log-hg hg-log-git &&
+ test_cmp git-log-hg git-log-git
+'
+
+test_expect_success 'converged merge' '
+ mkdir -p tmp && cd tmp &&
+ test_when_finished "cd .. && rm -rf tmp" &&
+
+ (
+ hg init hgrepo1 &&
+ cd hgrepo1 &&
+ echo A > afile &&
+ hg add afile &&
+ hg ci -m "origin" &&
+
+ echo B > afile &&
+ hg ci -m "A->B" &&
+
+ echo C > afile &&
+ hg ci -m "B->C" &&
+
+ hg up -r0 &&
+ echo C > afile &&
+ hg ci -m "A->C" &&
+
+ hg merge -r2 || true &&
+ hg ci -m "merge"
+ ) &&
+
+ for x in hg git; do
+ git_clone_$x hgrepo1 gitrepo-$x &&
+ hg_clone_$x gitrepo-$x hgrepo2-$x &&
+ hg_log hgrepo2-$x > hg-log-$x &&
+ git_log gitrepo-$x > git-log-$x
+ done &&
+
+ test_cmp hg-log-hg hg-log-git &&
+ test_cmp git-log-hg git-log-git
+'
+
+test_expect_success 'encoding' '
+ mkdir -p tmp && cd tmp &&
+ test_when_finished "cd .. && rm -rf tmp" &&
+
+ (
+ git init -q gitrepo &&
+ cd gitrepo &&
+
+ echo alpha > alpha &&
+ git add alpha &&
+ git commit -m "add älphà" &&
+
+ export GIT_AUTHOR_NAME="tést èncödîng" &&
+ echo beta > beta &&
+ git add beta &&
+ git commit -m "add beta" &&
+
+ echo gamma > gamma &&
+ git add gamma &&
+ git commit -m "add gämmâ" &&
+
+ : TODO git config i18n.commitencoding latin-1 &&
+ echo delta > delta &&
+ git add delta &&
+ git commit -m "add déltà"
+ ) &&
+
+ for x in hg git; do
+ hg_clone_$x gitrepo hgrepo-$x &&
+ git_clone_$x hgrepo-$x gitrepo2-$x &&
+
+ HGENCODING=utf-8 hg_log hgrepo-$x > hg-log-$x &&
+ git_log gitrepo2-$x > git-log-$x
+ done &&
+
+ test_cmp hg-log-hg hg-log-git &&
+ test_cmp git-log-hg git-log-git
+'
+
+test_expect_success 'file removal' '
+ mkdir -p tmp && cd tmp &&
+ test_when_finished "cd .. && rm -rf tmp" &&
+
+ (
+ git init -q gitrepo &&
+ cd gitrepo &&
+ echo alpha > alpha &&
+ git add alpha &&
+ git commit -m "add alpha" &&
+ echo beta > beta &&
+ git add beta &&
+ git commit -m "add beta"
+ mkdir foo &&
+ echo blah > foo/bar &&
+ git add foo &&
+ git commit -m "add foo" &&
+ git rm alpha &&
+ git commit -m "remove alpha" &&
+ git rm foo/bar &&
+ git commit -m "remove foo/bar"
+ ) &&
+
+ for x in hg git; do
+ (
+ hg_clone_$x gitrepo hgrepo-$x &&
+ cd hgrepo-$x &&
+ hg_log . &&
+ hg manifest -r 3 &&
+ hg manifest
+ ) > output-$x &&
+
+ git_clone_$x hgrepo-$x gitrepo2-$x &&
+ git_log gitrepo2-$x > log-$x
+ done &&
+
+ test_cmp output-hg output-git &&
+ test_cmp log-hg log-git
+'
+
+test_expect_success 'git tags' '
+ mkdir -p tmp && cd tmp &&
+ test_when_finished "cd .. && rm -rf tmp" &&
+
+ (
+ git init -q gitrepo &&
+ cd gitrepo &&
+ git config receive.denyCurrentBranch ignore &&
+ echo alpha > alpha &&
+ git add alpha &&
+ git commit -m "add alpha" &&
+ git tag alpha &&
+
+ echo beta > beta &&
+ git add beta &&
+ git commit -m "add beta" &&
+ git tag -a -m "added tag beta" beta
+ ) &&
+
+ for x in hg git; do
+ hg_clone_$x gitrepo hgrepo-$x &&
+ hg_log hgrepo-$x > log-$x
+ done &&
+
+ test_cmp log-hg log-git
+'
+
+test_expect_success 'hg author' '
+ mkdir -p tmp && cd tmp &&
+ test_when_finished "cd .. && rm -rf tmp" &&
+
+ for x in hg git; do
+ (
+ git init -q gitrepo-$x &&
+ cd gitrepo-$x &&
+
+ echo alpha > alpha &&
+ git add alpha &&
+ git commit -m "add alpha" &&
+ git checkout -q -b not-master
+ ) &&
+
+ (
+ hg_clone_$x gitrepo-$x hgrepo-$x &&
+ cd hgrepo-$x &&
+
+ hg co master &&
+ echo beta > beta &&
+ hg add beta &&
+ hg commit -u "test" -m "add beta" &&
+
+ echo gamma >> beta &&
+ hg commit -u "test <test@example.com> (comment)" -m "modify beta" &&
+
+ echo gamma > gamma &&
+ hg add gamma &&
+ hg commit -u "<test@example.com>" -m "add gamma" &&
+
+ echo delta > delta &&
+ hg add delta &&
+ hg commit -u "name<test@example.com>" -m "add delta" &&
+
+ echo epsilon > epsilon &&
+ hg add epsilon &&
+ hg commit -u "name <test@example.com" -m "add epsilon" &&
+
+ echo zeta > zeta &&
+ hg add zeta &&
+ hg commit -u " test " -m "add zeta" &&
+
+ echo eta > eta &&
+ hg add eta &&
+ hg commit -u "test < test@example.com >" -m "add eta" &&
+
+ echo theta > theta &&
+ hg add theta &&
+ hg commit -u "test >test@example.com>" -m "add theta"
+ ) &&
+
+ hg_push_$x hgrepo-$x gitrepo-$x &&
+ hg_clone_$x gitrepo-$x hgrepo2-$x &&
+
+ hg_log hgrepo2-$x > hg-log-$x &&
+ git_log gitrepo-$x > git-log-$x
+ done &&
+
+ test_cmp git-log-hg git-log-git &&
+
+ test_cmp hg-log-hg hg-log-git &&
+ test_cmp git-log-hg git-log-git
+'
+
+test_expect_success 'hg branch' '
+ mkdir -p tmp && cd tmp &&
+ test_when_finished "cd .. && rm -rf tmp" &&
+
+ for x in hg git; do
+ (
+ git init -q gitrepo-$x &&
+ cd gitrepo-$x &&
+
+ echo alpha > alpha &&
+ git add alpha &&
+ git commit -q -m "add alpha" &&
+ git checkout -q -b not-master
+ ) &&
+
+ (
+ hg_clone_$x gitrepo-$x hgrepo-$x &&
+
+ cd hgrepo-$x &&
+ hg -q co master &&
+ hg mv alpha beta &&
+ hg -q commit -m "rename alpha to beta" &&
+ hg branch gamma | grep -v "permanent and global" &&
+ hg -q commit -m "started branch gamma"
+ ) &&
+
+ hg_push_$x hgrepo-$x gitrepo-$x &&
+ hg_clone_$x gitrepo-$x hgrepo2-$x &&
+
+ hg_log hgrepo2-$x > hg-log-$x &&
+ git_log gitrepo-$x > git-log-$x
+ done &&
+
+ test_cmp hg-log-hg hg-log-git &&
+ test_cmp git-log-hg git-log-git
+'
+
+test_expect_success 'hg tags' '
+ mkdir -p tmp && cd tmp &&
+ test_when_finished "cd .. && rm -rf tmp" &&
+
+ for x in hg git; do
+ (
+ git init -q gitrepo-$x &&
+ cd gitrepo-$x &&
+
+ echo alpha > alpha &&
+ git add alpha &&
+ git commit -m "add alpha" &&
+ git checkout -q -b not-master
+ ) &&
+
+ (
+ hg_clone_$x gitrepo-$x hgrepo-$x &&
+
+ cd hgrepo-$x &&
+ hg co master &&
+ hg tag alpha
+ ) &&
+
+ hg_push_$x hgrepo-$x gitrepo-$x &&
+ hg_clone_$x gitrepo-$x hgrepo2-$x &&
+
+ (
+ git --git-dir=gitrepo-$x/.git tag -l &&
+ hg_log hgrepo2-$x &&
+ cat hgrepo2-$x/.hgtags
+ ) > output-$x
+ done &&
+
+ test_cmp output-hg output-git
+'
+
+test_done
--
1.8.0
^ permalink raw reply related
* [PATCH v6 12/16] remote-hg: add bidirectional tests
From: Felipe Contreras @ 2012-11-04 2:13 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Jeff King, Sverre Rabbelier, Johannes Schindelin,
Ilari Liusvaara, Daniel Barkalow, Michael J Gruber,
Felipe Contreras
In-Reply-To: <1351995218-19889-1-git-send-email-felipe.contreras@gmail.com>
Base commands from hg-git tests:
https://bitbucket.org/durin42/hg-git/src
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
contrib/remote-helpers/test-hg-bidi.sh | 243 +++++++++++++++++++++++++++++++++
1 file changed, 243 insertions(+)
create mode 100755 contrib/remote-helpers/test-hg-bidi.sh
diff --git a/contrib/remote-helpers/test-hg-bidi.sh b/contrib/remote-helpers/test-hg-bidi.sh
new file mode 100755
index 0000000..a94eb28
--- /dev/null
+++ b/contrib/remote-helpers/test-hg-bidi.sh
@@ -0,0 +1,243 @@
+#!/bin/sh
+#
+# Copyright (c) 2012 Felipe Contreras
+#
+# Base commands from hg-git tests:
+# https://bitbucket.org/durin42/hg-git/src
+#
+
+test_description='Test biridectionality of remote-hg'
+
+. ./test-lib.sh
+
+if ! test_have_prereq PYTHON; then
+ skip_all='skipping remote-hg tests; python not available'
+ test_done
+fi
+
+if ! "$PYTHON_PATH" -c 'import mercurial'; then
+ skip_all='skipping remote-hg tests; mercurial not available'
+ test_done
+fi
+
+# clone to a git repo
+git_clone () {
+ hg -R $1 bookmark -f -r tip master &&
+ git clone -q "hg::$PWD/$1" $2
+}
+
+# clone to an hg repo
+hg_clone () {
+ (
+ hg init $2 &&
+ cd $1 &&
+ git push -q "hg::$PWD/../$2" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*'
+ ) &&
+
+ (cd $2 && hg -q update)
+}
+
+# push an hg repo
+hg_push () {
+ (
+ cd $2
+ old=$(git symbolic-ref --short HEAD)
+ git checkout -q -b tmp &&
+ git fetch -q "hg::$PWD/../$1" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*' &&
+ git checkout -q $old &&
+ git branch -q -D tmp 2> /dev/null || true
+ )
+}
+
+hg_log () {
+ hg -R $1 log --graph --debug | grep -v 'tag: *default/'
+}
+
+setup () {
+ (
+ echo "[ui]"
+ echo "username = A U Thor <author@example.com>"
+ echo "[defaults]"
+ echo "backout = -d \"0 0\""
+ echo "commit = -d \"0 0\""
+ echo "debugrawcommit = -d \"0 0\""
+ echo "tag = -d \"0 0\""
+ ) >> "$HOME"/.hgrc &&
+ git config --global remote-hg.hg-git-compat true
+
+ export HGEDITOR=/usr/bin/true
+
+ export GIT_AUTHOR_DATE="2007-01-01 00:00:00 +0230"
+ export GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
+}
+
+setup
+
+test_expect_success 'encoding' '
+ mkdir -p tmp && cd tmp &&
+ test_when_finished "cd .. && rm -rf tmp" &&
+
+ (
+ git init -q gitrepo &&
+ cd gitrepo &&
+
+ echo alpha > alpha &&
+ git add alpha &&
+ git commit -m "add älphà" &&
+
+ export GIT_AUTHOR_NAME="tést èncödîng" &&
+ echo beta > beta &&
+ git add beta &&
+ git commit -m "add beta" &&
+
+ echo gamma > gamma &&
+ git add gamma &&
+ git commit -m "add gämmâ" &&
+
+ : TODO git config i18n.commitencoding latin-1 &&
+ echo delta > delta &&
+ git add delta &&
+ git commit -m "add déltà"
+ ) &&
+
+ hg_clone gitrepo hgrepo &&
+ git_clone hgrepo gitrepo2 &&
+ hg_clone gitrepo2 hgrepo2 &&
+
+ HGENCODING=utf-8 hg_log hgrepo > expected &&
+ HGENCODING=utf-8 hg_log hgrepo2 > actual &&
+
+ test_cmp expected actual
+'
+
+test_expect_success 'file removal' '
+ mkdir -p tmp && cd tmp &&
+ test_when_finished "cd .. && rm -rf tmp" &&
+
+ (
+ git init -q gitrepo &&
+ cd gitrepo &&
+ echo alpha > alpha &&
+ git add alpha &&
+ git commit -m "add alpha" &&
+ echo beta > beta &&
+ git add beta &&
+ git commit -m "add beta"
+ mkdir foo &&
+ echo blah > foo/bar &&
+ git add foo &&
+ git commit -m "add foo" &&
+ git rm alpha &&
+ git commit -m "remove alpha" &&
+ git rm foo/bar &&
+ git commit -m "remove foo/bar"
+ ) &&
+
+ hg_clone gitrepo hgrepo &&
+ git_clone hgrepo gitrepo2 &&
+ hg_clone gitrepo2 hgrepo2 &&
+
+ hg_log hgrepo > expected &&
+ hg_log hgrepo2 > actual &&
+
+ test_cmp expected actual
+'
+
+test_expect_success 'git tags' '
+ mkdir -p tmp && cd tmp &&
+ test_when_finished "cd .. && rm -rf tmp" &&
+
+ (
+ git init -q gitrepo &&
+ cd gitrepo &&
+ git config receive.denyCurrentBranch ignore &&
+ echo alpha > alpha &&
+ git add alpha &&
+ git commit -m "add alpha" &&
+ git tag alpha &&
+
+ echo beta > beta &&
+ git add beta &&
+ git commit -m "add beta" &&
+ git tag -a -m "added tag beta" beta
+ ) &&
+
+ hg_clone gitrepo hgrepo &&
+ git_clone hgrepo gitrepo2 &&
+ hg_clone gitrepo2 hgrepo2 &&
+
+ hg_log hgrepo > expected &&
+ hg_log hgrepo2 > actual &&
+
+ test_cmp expected actual
+'
+
+test_expect_success 'hg branch' '
+ mkdir -p tmp && cd tmp &&
+ test_when_finished "cd .. && rm -rf tmp" &&
+
+ (
+ git init -q gitrepo &&
+ cd gitrepo &&
+
+ echo alpha > alpha &&
+ git add alpha &&
+ git commit -q -m "add alpha" &&
+ git checkout -q -b not-master
+ ) &&
+
+ (
+ hg_clone gitrepo hgrepo &&
+
+ cd hgrepo &&
+ hg -q co master &&
+ hg mv alpha beta &&
+ hg -q commit -m "rename alpha to beta" &&
+ hg branch gamma | grep -v "permanent and global" &&
+ hg -q commit -m "started branch gamma"
+ ) &&
+
+ hg_push hgrepo gitrepo &&
+ hg_clone gitrepo hgrepo2 &&
+
+ : TODO, avoid "master" bookmark &&
+ (cd hgrepo2 && hg checkout gamma) &&
+
+ hg_log hgrepo > expected &&
+ hg_log hgrepo2 > actual &&
+
+ test_cmp expected actual
+'
+
+test_expect_success 'hg tags' '
+ mkdir -p tmp && cd tmp &&
+ test_when_finished "cd .. && rm -rf tmp" &&
+
+ (
+ git init -q gitrepo &&
+ cd gitrepo &&
+
+ echo alpha > alpha &&
+ git add alpha &&
+ git commit -m "add alpha" &&
+ git checkout -q -b not-master
+ ) &&
+
+ (
+ hg_clone gitrepo hgrepo &&
+
+ cd hgrepo &&
+ hg co master &&
+ hg tag alpha
+ ) &&
+
+ hg_push hgrepo gitrepo &&
+ hg_clone gitrepo hgrepo2 &&
+
+ hg_log hgrepo > expected &&
+ hg_log hgrepo2 > actual &&
+
+ test_cmp expected actual
+'
+
+test_done
--
1.8.0
^ permalink raw reply related
* [PATCH v6 11/16] test-lib: avoid full path to store test results
From: Felipe Contreras @ 2012-11-04 2:13 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Jeff King, Sverre Rabbelier, Johannes Schindelin,
Ilari Liusvaara, Daniel Barkalow, Michael J Gruber,
Felipe Contreras
In-Reply-To: <1351995218-19889-1-git-send-email-felipe.contreras@gmail.com>
No reason to use the full path in case this is used externally.
Otherwise we might get errors such as:
./test-lib.sh: line 394: /home/bob/dev/git/t/test-results//home/bob/dev/git/contrib/remote-hg/test-2894.counts: No such file or directory
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
t/test-lib.sh | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 514282c..5a3d665 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -389,7 +389,8 @@ test_done () {
then
test_results_dir="$TEST_OUTPUT_DIRECTORY/test-results"
mkdir -p "$test_results_dir"
- test_results_path="$test_results_dir/${0%.sh}-$$.counts"
+ base=${0##*/}
+ test_results_path="$test_results_dir/${base%.sh}-$$.counts"
cat >>"$test_results_path" <<-EOF
total $test_count
--
1.8.0
^ permalink raw reply related
* [PATCH v6 10/16] remote-hg: add basic tests
From: Felipe Contreras @ 2012-11-04 2:13 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Jeff King, Sverre Rabbelier, Johannes Schindelin,
Ilari Liusvaara, Daniel Barkalow, Michael J Gruber,
Felipe Contreras
In-Reply-To: <1351995218-19889-1-git-send-email-felipe.contreras@gmail.com>
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
contrib/remote-helpers/Makefile | 13 +++++
contrib/remote-helpers/test-hg.sh | 112 ++++++++++++++++++++++++++++++++++++++
2 files changed, 125 insertions(+)
create mode 100644 contrib/remote-helpers/Makefile
create mode 100755 contrib/remote-helpers/test-hg.sh
diff --git a/contrib/remote-helpers/Makefile b/contrib/remote-helpers/Makefile
new file mode 100644
index 0000000..9a76575
--- /dev/null
+++ b/contrib/remote-helpers/Makefile
@@ -0,0 +1,13 @@
+TESTS := $(wildcard test*.sh)
+
+export T := $(addprefix $(CURDIR)/,$(TESTS))
+export MAKE := $(MAKE) -e
+export PATH := $(CURDIR):$(PATH)
+
+test:
+ $(MAKE) -C ../../t $@
+
+$(TESTS):
+ $(MAKE) -C ../../t $(CURDIR)/$@
+
+.PHONY: $(TESTS)
diff --git a/contrib/remote-helpers/test-hg.sh b/contrib/remote-helpers/test-hg.sh
new file mode 100755
index 0000000..40e6e3c
--- /dev/null
+++ b/contrib/remote-helpers/test-hg.sh
@@ -0,0 +1,112 @@
+#!/bin/sh
+#
+# Copyright (c) 2012 Felipe Contreras
+#
+# Base commands from hg-git tests:
+# https://bitbucket.org/durin42/hg-git/src
+#
+
+test_description='Test remote-hg'
+
+. ./test-lib.sh
+
+if ! test_have_prereq PYTHON; then
+ skip_all='skipping remote-hg tests; python not available'
+ test_done
+fi
+
+if ! "$PYTHON_PATH" -c 'import mercurial'; then
+ skip_all='skipping remote-hg tests; mercurial not available'
+ test_done
+fi
+
+check () {
+ (cd $1 &&
+ git log --format='%s' -1 &&
+ git symbolic-ref HEAD) > actual &&
+ (echo $2 &&
+ echo "refs/heads/$3") > expected &&
+ test_cmp expected actual
+}
+
+test_expect_success 'cloning' '
+ test_when_finished "rm -rf gitrepo*" &&
+
+ (
+ hg init hgrepo &&
+ cd hgrepo &&
+ echo zero > content &&
+ hg add content &&
+ hg commit -m zero
+ ) &&
+
+ git clone "hg::$PWD/hgrepo" gitrepo &&
+ check gitrepo zero master
+'
+
+test_expect_success 'cloning with branches' '
+ test_when_finished "rm -rf gitrepo*" &&
+
+ (
+ cd hgrepo &&
+ hg branch next &&
+ echo next > content &&
+ hg commit -m next
+ ) &&
+
+ git clone "hg::$PWD/hgrepo" gitrepo &&
+ check gitrepo next next &&
+
+ (cd hgrepo && hg checkout default) &&
+
+ git clone "hg::$PWD/hgrepo" gitrepo2 &&
+ check gitrepo2 zero master
+'
+
+test_expect_success 'cloning with bookmarks' '
+ test_when_finished "rm -rf gitrepo*" &&
+
+ (
+ cd hgrepo &&
+ hg bookmark feature-a &&
+ echo feature-a > content &&
+ hg commit -m feature-a
+ ) &&
+
+ git clone "hg::$PWD/hgrepo" gitrepo &&
+ check gitrepo feature-a feature-a
+'
+
+test_expect_success 'cloning with detached head' '
+ test_when_finished "rm -rf gitrepo*" &&
+
+ (
+ cd hgrepo &&
+ hg update -r 0
+ ) &&
+
+ git clone "hg::$PWD/hgrepo" gitrepo &&
+ check gitrepo zero master
+'
+
+test_expect_success 'update bookmark' '
+ test_when_finished "rm -rf gitrepo*" &&
+
+ (
+ cd hgrepo &&
+ hg bookmark devel
+ ) &&
+
+ (
+ git clone "hg::$PWD/hgrepo" gitrepo &&
+ cd gitrepo &&
+ git checkout devel &&
+ echo devel > content &&
+ git commit -a -m devel &&
+ git push
+ ) &&
+
+ hg -R hgrepo bookmarks | grep "devel\s\+3:"
+'
+
+test_done
--
1.8.0
^ permalink raw reply related
* [PATCH v6 09/16] remote-hg: fake bookmark when there's none
From: Felipe Contreras @ 2012-11-04 2:13 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Jeff King, Sverre Rabbelier, Johannes Schindelin,
Ilari Liusvaara, Daniel Barkalow, Michael J Gruber,
Felipe Contreras
In-Reply-To: <1351995218-19889-1-git-send-email-felipe.contreras@gmail.com>
Or at least no current bookmark.
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
contrib/remote-helpers/git-remote-hg | 19 ++++++++++++++-----
1 file changed, 14 insertions(+), 5 deletions(-)
diff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg
index 9db4b7e..dbe309a 100755
--- a/contrib/remote-helpers/git-remote-hg
+++ b/contrib/remote-helpers/git-remote-hg
@@ -26,7 +26,7 @@ import urllib
# git:
# Sensible defaults for git.
# hg bookmarks are exported as git branches, hg branches are prefixed
-# with 'branches/'.
+# with 'branches/', HEAD is a special case.
#
# hg:
# Emulate hg-git.
@@ -430,12 +430,21 @@ def get_branch_tip(repo, branch):
return heads[0]
def list_head(repo, cur):
- global g_head
+ global g_head, bmarks
head = bookmarks.readcurrent(repo)
- if not head:
- return
- node = repo[head]
+ if head:
+ node = repo[head]
+ else:
+ # fake bookmark from current branch
+ head = cur
+ node = repo['.']
+ if not node:
+ return
+ if head == 'default':
+ head = 'master'
+ bmarks[head] = node
+
print "@refs/heads/%s HEAD" % head
g_head = (head, node)
--
1.8.0
^ permalink raw reply related
* [PATCH v6 08/16] remote-hg: add compat for hg-git author fixes
From: Felipe Contreras @ 2012-11-04 2:13 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Jeff King, Sverre Rabbelier, Johannes Schindelin,
Ilari Liusvaara, Daniel Barkalow, Michael J Gruber,
Felipe Contreras
In-Reply-To: <1351995218-19889-1-git-send-email-felipe.contreras@gmail.com>
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
contrib/remote-helpers/git-remote-hg | 59 ++++++++++++++++++++++++++++++++----
1 file changed, 53 insertions(+), 6 deletions(-)
diff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg
index d585756..9db4b7e 100755
--- a/contrib/remote-helpers/git-remote-hg
+++ b/contrib/remote-helpers/git-remote-hg
@@ -17,6 +17,7 @@ import os
import json
import shutil
import subprocess
+import urllib
#
# If you want to switch to hg-git compatibility mode:
@@ -35,6 +36,7 @@ import subprocess
NAME_RE = re.compile('^([^<>]+)')
AUTHOR_RE = re.compile('^([^<>]+?)? ?<([^<>]+)>$')
+AUTHOR_HG_RE = re.compile('^(.*?) ?<(.+?)(?:>(.+)?)?$')
RAW_AUTHOR_RE = re.compile('^(\w+) (?:(.+)? )?<(.+)> (\d+) ([+-]\d+)')
def die(msg, *args):
@@ -152,12 +154,20 @@ class Parser:
return sys.stdin.read(size)
def get_author(self):
+ global bad_mail
+
+ ex = None
m = RAW_AUTHOR_RE.match(self.line)
if not m:
return None
_, name, email, date, tz = m.groups()
+ if name and 'ext:' in name:
+ m = re.match('^(.+?) ext:\((.+)\)$', name)
+ if m:
+ name = m.group(1)
+ ex = urllib.unquote(m.group(2))
- if email != 'unknown':
+ if email != bad_mail:
if name:
user = '%s <%s>' % (name, email)
else:
@@ -165,6 +175,9 @@ class Parser:
else:
user = name
+ if ex:
+ user += ex
+
tz = int(tz)
tz = ((tz / 100) * 3600) + ((tz % 100) * 60)
return (user, int(date), -tz)
@@ -194,9 +207,9 @@ def get_filechanges(repo, ctx, parent):
return added | modified, removed
-def fixup_user(user):
- user = user.replace('"', '')
+def fixup_user_git(user):
name = mail = None
+ user = user.replace('"', '')
m = AUTHOR_RE.match(user)
if m:
name = m.group(1)
@@ -205,11 +218,41 @@ def fixup_user(user):
m = NAME_RE.match(user)
if m:
name = m.group(1).strip()
+ return (name, mail)
+
+def fixup_user_hg(user):
+ def sanitize(name):
+ # stole this from hg-git
+ return re.sub('[<>\n]', '?', name.lstrip('< ').rstrip('> '))
+
+ m = AUTHOR_HG_RE.match(user)
+ if m:
+ name = sanitize(m.group(1))
+ mail = sanitize(m.group(2))
+ ex = m.group(3)
+ if ex:
+ name += ' ext:(' + urllib.quote(ex) + ')'
+ else:
+ name = sanitize(user)
+ if '@' in user:
+ mail = name
+ else:
+ mail = None
+
+ return (name, mail)
+
+def fixup_user(user):
+ global mode, bad_mail
+
+ if mode == 'git':
+ name, mail = fixup_user_git(user)
+ else:
+ name, mail = fixup_user_hg(user)
if not name:
- name = 'Unknown'
+ name = bad_name
if not mail:
- mail = 'unknown'
+ mail = bad_mail
return '%s <%s>' % (name, mail)
@@ -660,7 +703,7 @@ def do_export(parser):
def main(args):
global prefix, dirname, branches, bmarks
global marks, blob_marks, parsed_refs
- global peer, mode
+ global peer, mode, bad_mail, bad_name
alias = args[1]
url = args[2]
@@ -676,8 +719,12 @@ def main(args):
if hg_git_compat:
mode = 'hg'
+ bad_mail = 'none@none'
+ bad_name = ''
else:
mode = 'git'
+ bad_mail = 'unknown'
+ bad_name = 'Unknown'
if alias[4:] == url:
is_tmp = True
--
1.8.0
^ permalink raw reply related
* [PATCH v6 06/16] remote-hg: match hg merge behavior
From: Felipe Contreras @ 2012-11-04 2:13 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Jeff King, Sverre Rabbelier, Johannes Schindelin,
Ilari Liusvaara, Daniel Barkalow, Michael J Gruber,
Felipe Contreras
In-Reply-To: <1351995218-19889-1-git-send-email-felipe.contreras@gmail.com>
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
contrib/remote-helpers/git-remote-hg | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg
index 503a9fc..247b7cb 100755
--- a/contrib/remote-helpers/git-remote-hg
+++ b/contrib/remote-helpers/git-remote-hg
@@ -427,6 +427,14 @@ def parse_blob(parser):
parser.next()
return
+def get_merge_files(repo, p1, p2, files):
+ for e in repo[p1].files():
+ if e not in files:
+ if e not in repo[p1].manifest():
+ continue
+ f = { 'ctx' : repo[p1][e] }
+ files[e] = f
+
def parse_commit(parser):
global marks, blob_marks, bmarks, parsed_refs
@@ -470,6 +478,8 @@ def parse_commit(parser):
of = files[f]
if 'deleted' in of:
raise IOError
+ if 'ctx' in of:
+ return of['ctx']
is_exec = of['mode'] == 'x'
is_link = of['mode'] == 'l'
return context.memfilectx(f, of['data'], is_link, is_exec, None)
@@ -492,6 +502,13 @@ def parse_commit(parser):
else:
p2 = '\0' * 20
+ #
+ # If files changed from any of the parents, hg wants to know, but in git if
+ # nothing changed from the first parent, nothing changed.
+ #
+ if merge_mark:
+ get_merge_files(repo, p1, p2, files)
+
ctx = context.memctx(repo, (p1, p2), data,
files.keys(), getfilectx,
user, (date, tz), extra)
--
1.8.0
^ permalink raw reply related
* [PATCH v6 07/16] remote-hg: add support for hg-git compat mode
From: Felipe Contreras @ 2012-11-04 2:13 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Jeff King, Sverre Rabbelier, Johannes Schindelin,
Ilari Liusvaara, Daniel Barkalow, Michael J Gruber,
Felipe Contreras
In-Reply-To: <1351995218-19889-1-git-send-email-felipe.contreras@gmail.com>
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
contrib/remote-helpers/git-remote-hg | 89 +++++++++++++++++++++++++++++++++---
1 file changed, 83 insertions(+), 6 deletions(-)
diff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg
index 247b7cb..d585756 100755
--- a/contrib/remote-helpers/git-remote-hg
+++ b/contrib/remote-helpers/git-remote-hg
@@ -16,6 +16,22 @@ import sys
import os
import json
import shutil
+import subprocess
+
+#
+# If you want to switch to hg-git compatibility mode:
+# git config --global remote-hg.hg-git-compat true
+#
+# git:
+# Sensible defaults for git.
+# hg bookmarks are exported as git branches, hg branches are prefixed
+# with 'branches/'.
+#
+# hg:
+# Emulate hg-git.
+# Only hg bookmarks are exported as git branches.
+# Commits are modified to preserve hg information and allow biridectionality.
+#
NAME_RE = re.compile('^([^<>]+)')
AUTHOR_RE = re.compile('^([^<>]+?)? ?<([^<>]+)>$')
@@ -226,7 +242,7 @@ def mark_to_rev(mark):
return marks.to_rev(mark)
def export_ref(repo, name, kind, head):
- global prefix, marks
+ global prefix, marks, mode
ename = '%s/%s' % (kind, name)
tip = marks.get_tip(ename)
@@ -261,6 +277,33 @@ def export_ref(repo, name, kind, head):
else:
modified, removed = get_filechanges(repo, c, parents[0])
+ if mode == 'hg':
+ extra_msg = ''
+
+ if rev_branch != 'default':
+ extra_msg += 'branch : %s\n' % rev_branch
+
+ renames = []
+ for f in c.files():
+ if f not in c.manifest():
+ continue
+ rename = c.filectx(f).renamed()
+ if rename:
+ renames.append((rename[0], f))
+
+ for e in renames:
+ extra_msg += "rename : %s => %s\n" % e
+
+ for key, value in extra.iteritems():
+ if key in ('author', 'committer', 'encoding', 'message', 'branch', 'hg-git'):
+ continue
+ else:
+ extra_msg += "extra : %s : %s\n" % (key, urllib.quote(value))
+
+ desc += '\n'
+ if extra_msg:
+ desc += '\n--HG--\n' + extra_msg
+
if len(parents) == 0 and rev:
print 'reset %s/%s' % (prefix, ename)
@@ -354,7 +397,7 @@ def list_head(repo, cur):
g_head = (head, node)
def do_list(parser):
- global branches, bmarks
+ global branches, bmarks, mode
repo = parser.repo
for branch in repo.branchmap():
@@ -368,8 +411,11 @@ def do_list(parser):
cur = repo.dirstate.branch()
list_head(repo, cur)
- for branch in branches:
- print "? refs/heads/branches/%s" % branch
+
+ if mode != 'hg':
+ for branch in branches:
+ print "? refs/heads/branches/%s" % branch
+
for bmark in bmarks:
print "? refs/heads/%s" % bmark
@@ -437,6 +483,7 @@ def get_merge_files(repo, p1, p2, files):
def parse_commit(parser):
global marks, blob_marks, bmarks, parsed_refs
+ global mode
from_mark = merge_mark = None
@@ -482,7 +529,9 @@ def parse_commit(parser):
return of['ctx']
is_exec = of['mode'] == 'x'
is_link = of['mode'] == 'l'
- return context.memfilectx(f, of['data'], is_link, is_exec, None)
+ rename = of.get('rename', None)
+ return context.memfilectx(f, of['data'],
+ is_link, is_exec, rename)
repo = parser.repo
@@ -509,6 +558,21 @@ def parse_commit(parser):
if merge_mark:
get_merge_files(repo, p1, p2, files)
+ if mode == 'hg':
+ i = data.find('\n--HG--\n')
+ if i >= 0:
+ tmp = data[i + len('\n--HG--\n'):].strip()
+ for k, v in [e.split(' : ') for e in tmp.split('\n')]:
+ if k == 'rename':
+ old, new = v.split(' => ', 1)
+ files[new]['rename'] = old
+ elif k == 'branch':
+ extra[k] = v
+ elif k == 'extra':
+ ek, ev = v.split(' : ', 1)
+ extra[ek] = urllib.unquote(ev)
+ data = data[:i]
+
ctx = context.memctx(repo, (p1, p2), data,
files.keys(), getfilectx,
user, (date, tz), extra)
@@ -596,12 +660,25 @@ def do_export(parser):
def main(args):
global prefix, dirname, branches, bmarks
global marks, blob_marks, parsed_refs
- global peer
+ global peer, mode
alias = args[1]
url = args[2]
peer = None
+ cmd = ['git', 'config', '--get', 'remote-hg.hg-git-compat']
+ hg_git_compat = False
+ try:
+ if subprocess.check_output(cmd) == 'true\n':
+ hg_git_compat = True
+ except subprocess.CalledProcessError:
+ pass
+
+ if hg_git_compat:
+ mode = 'hg'
+ else:
+ mode = 'git'
+
if alias[4:] == url:
is_tmp = True
alias = util.sha1(alias).hexdigest()
--
1.8.0
^ permalink raw reply related
* [PATCH v6 05/16] remote-hg: make sure the encoding is correct
From: Felipe Contreras @ 2012-11-04 2:13 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Jeff King, Sverre Rabbelier, Johannes Schindelin,
Ilari Liusvaara, Daniel Barkalow, Michael J Gruber,
Felipe Contreras
In-Reply-To: <1351995218-19889-1-git-send-email-felipe.contreras@gmail.com>
Independently of the environment.
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
contrib/remote-helpers/git-remote-hg | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg
index a5023c9..503a9fc 100755
--- a/contrib/remote-helpers/git-remote-hg
+++ b/contrib/remote-helpers/git-remote-hg
@@ -9,7 +9,7 @@
# Then you can clone with:
# git clone hg::/path/to/mercurial/repo/
-from mercurial import hg, ui, bookmarks, context, util
+from mercurial import hg, ui, bookmarks, context, util, encoding
import re
import sys
@@ -391,6 +391,9 @@ def do_import(parser):
print "feature export-marks=%s" % path
sys.stdout.flush()
+ tmp = encoding.encoding
+ encoding.encoding = 'utf-8'
+
# lets get all the import lines
while parser.check('import'):
ref = parser[1]
@@ -409,6 +412,8 @@ def do_import(parser):
parser.next()
+ encoding.encoding = tmp
+
print 'done'
def parse_blob(parser):
@@ -491,8 +496,13 @@ def parse_commit(parser):
files.keys(), getfilectx,
user, (date, tz), extra)
+ tmp = encoding.encoding
+ encoding.encoding = 'utf-8'
+
node = repo.commitctx(ctx)
+ encoding.encoding = tmp
+
rev = repo[node].rev()
parsed_refs[ref] = node
--
1.8.0
^ permalink raw reply related
* [PATCH v6 04/16] remote-hg: add support to push URLs
From: Felipe Contreras @ 2012-11-04 2:13 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Jeff King, Sverre Rabbelier, Johannes Schindelin,
Ilari Liusvaara, Daniel Barkalow, Michael J Gruber,
Felipe Contreras
In-Reply-To: <1351995218-19889-1-git-send-email-felipe.contreras@gmail.com>
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
contrib/remote-helpers/git-remote-hg | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg
index 45629e0..a5023c9 100755
--- a/contrib/remote-helpers/git-remote-hg
+++ b/contrib/remote-helpers/git-remote-hg
@@ -9,12 +9,13 @@
# Then you can clone with:
# git clone hg::/path/to/mercurial/repo/
-from mercurial import hg, ui, bookmarks, context
+from mercurial import hg, ui, bookmarks, context, util
import re
import sys
import os
import json
+import shutil
NAME_RE = re.compile('^([^<>]+)')
AUTHOR_RE = re.compile('^([^<>]+?)? ?<([^<>]+)>$')
@@ -574,6 +575,12 @@ def main(args):
url = args[2]
peer = None
+ if alias[4:] == url:
+ is_tmp = True
+ alias = util.sha1(alias).hexdigest()
+ else:
+ is_tmp = False
+
gitdir = os.environ['GIT_DIR']
dirname = os.path.join(gitdir, 'hg', alias)
branches = {}
@@ -604,6 +611,9 @@ def main(args):
die('unhandled command: %s' % line)
sys.stdout.flush()
- marks.store()
+ if not is_tmp:
+ marks.store()
+ else:
+ shutil.rmtree(dirname)
sys.exit(main(sys.argv))
--
1.8.0
^ permalink raw reply related
* [PATCH v6 02/16] remote-hg: add support for pushing
From: Felipe Contreras @ 2012-11-04 2:13 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Jeff King, Sverre Rabbelier, Johannes Schindelin,
Ilari Liusvaara, Daniel Barkalow, Michael J Gruber,
Felipe Contreras
In-Reply-To: <1351995218-19889-1-git-send-email-felipe.contreras@gmail.com>
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
contrib/remote-helpers/git-remote-hg | 217 ++++++++++++++++++++++++++++++++++-
1 file changed, 215 insertions(+), 2 deletions(-)
diff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg
index e37e278..fcceede 100755
--- a/contrib/remote-helpers/git-remote-hg
+++ b/contrib/remote-helpers/git-remote-hg
@@ -9,7 +9,7 @@
# Then you can clone with:
# git clone hg::/path/to/mercurial/repo/
-from mercurial import hg, ui, bookmarks
+from mercurial import hg, ui, bookmarks, context
import re
import sys
@@ -18,6 +18,7 @@ import json
NAME_RE = re.compile('^([^<>]+)')
AUTHOR_RE = re.compile('^([^<>]+?)? ?<([^<>]+)>$')
+RAW_AUTHOR_RE = re.compile('^(\w+) (?:(.+)? )?<(.+)> (\d+) ([+-]\d+)')
def die(msg, *args):
sys.stderr.write('ERROR: %s\n' % (msg % args))
@@ -32,12 +33,17 @@ def gitmode(flags):
def gittz(tz):
return '%+03d%02d' % (-tz / 3600, -tz % 3600 / 60)
+def hgmode(mode):
+ m = { '0100755': 'x', '0120000': 'l' }
+ return m.get(mode, '')
+
class Marks:
def __init__(self, path):
self.path = path
self.tips = {}
self.marks = {}
+ self.rev_marks = {}
self.last_mark = 0
self.load()
@@ -52,6 +58,9 @@ class Marks:
self.marks = tmp['marks']
self.last_mark = tmp['last-mark']
+ for rev, mark in self.marks.iteritems():
+ self.rev_marks[mark] = int(rev)
+
def dict(self):
return { 'tips': self.tips, 'marks': self.marks, 'last-mark' : self.last_mark }
@@ -64,11 +73,19 @@ class Marks:
def from_rev(self, rev):
return self.marks[str(rev)]
+ def to_rev(self, mark):
+ return self.rev_marks[mark]
+
def get_mark(self, rev):
self.last_mark += 1
self.marks[str(rev)] = self.last_mark
return self.last_mark
+ def new_mark(self, rev, mark):
+ self.marks[str(rev)] = mark
+ self.rev_marks[mark] = rev
+ self.last_mark = mark
+
def is_marked(self, rev):
return self.marks.has_key(str(rev))
@@ -106,6 +123,35 @@ class Parser:
if self.line == 'done':
self.line = None
+ def get_mark(self):
+ i = self.line.index(':') + 1
+ return int(self.line[i:])
+
+ def get_data(self):
+ if not self.check('data'):
+ return None
+ i = self.line.index(' ') + 1
+ size = int(self.line[i:])
+ return sys.stdin.read(size)
+
+ def get_author(self):
+ m = RAW_AUTHOR_RE.match(self.line)
+ if not m:
+ return None
+ _, name, email, date, tz = m.groups()
+
+ if email != 'unknown':
+ if name:
+ user = '%s <%s>' % (name, email)
+ else:
+ user = '<%s>' % (email)
+ else:
+ user = name
+
+ tz = int(tz)
+ tz = ((tz / 100) * 3600) + ((tz % 100) * 60)
+ return (user, int(date), -tz)
+
def export_file(fc):
d = fc.data()
print "M %s inline %s" % (gitmode(fc.flags()), fc.path())
@@ -174,6 +220,10 @@ def rev_to_mark(rev):
global marks
return marks.from_rev(rev)
+def mark_to_rev(mark):
+ global marks
+ return marks.to_rev(mark)
+
def export_ref(repo, name, kind, head):
global prefix, marks
@@ -263,9 +313,17 @@ def do_capabilities(parser):
global prefix, dirname
print "import"
+ print "export"
print "refspec refs/heads/branches/*:%s/branches/*" % prefix
print "refspec refs/heads/*:%s/bookmarks/*" % prefix
print "refspec refs/tags/*:%s/tags/*" % prefix
+
+ path = os.path.join(dirname, 'marks-git')
+
+ if os.path.exists(path):
+ print "*import-marks %s" % path
+ print "*export-marks %s" % path
+
print
def get_branch_tip(repo, branch):
@@ -352,8 +410,161 @@ def do_import(parser):
print 'done'
+def parse_blob(parser):
+ global blob_marks
+
+ parser.next()
+ mark = parser.get_mark()
+ parser.next()
+ data = parser.get_data()
+ blob_marks[mark] = data
+ parser.next()
+ return
+
+def parse_commit(parser):
+ global marks, blob_marks, bmarks, parsed_refs
+
+ from_mark = merge_mark = None
+
+ ref = parser[1]
+ parser.next()
+
+ commit_mark = parser.get_mark()
+ parser.next()
+ author = parser.get_author()
+ parser.next()
+ committer = parser.get_author()
+ parser.next()
+ data = parser.get_data()
+ parser.next()
+ if parser.check('from'):
+ from_mark = parser.get_mark()
+ parser.next()
+ if parser.check('merge'):
+ merge_mark = parser.get_mark()
+ parser.next()
+ if parser.check('merge'):
+ die('octopus merges are not supported yet')
+
+ files = {}
+
+ for line in parser:
+ if parser.check('M'):
+ t, m, mark_ref, path = line.split(' ')
+ mark = int(mark_ref[1:])
+ f = { 'mode' : hgmode(m), 'data' : blob_marks[mark] }
+ elif parser.check('D'):
+ t, path = line.split(' ')
+ f = { 'deleted' : True }
+ else:
+ die('Unknown file command: %s' % line)
+ files[path] = f
+
+ def getfilectx(repo, memctx, f):
+ of = files[f]
+ if 'deleted' in of:
+ raise IOError
+ is_exec = of['mode'] == 'x'
+ is_link = of['mode'] == 'l'
+ return context.memfilectx(f, of['data'], is_link, is_exec, None)
+
+ repo = parser.repo
+
+ user, date, tz = author
+ extra = {}
+
+ if committer != author:
+ extra['committer'] = "%s %u %u" % committer
+
+ if from_mark:
+ p1 = repo.changelog.node(mark_to_rev(from_mark))
+ else:
+ p1 = '\0' * 20
+
+ if merge_mark:
+ p2 = repo.changelog.node(mark_to_rev(merge_mark))
+ else:
+ p2 = '\0' * 20
+
+ ctx = context.memctx(repo, (p1, p2), data,
+ files.keys(), getfilectx,
+ user, (date, tz), extra)
+
+ node = repo.commitctx(ctx)
+
+ rev = repo[node].rev()
+
+ parsed_refs[ref] = node
+
+ marks.new_mark(rev, commit_mark)
+
+def parse_reset(parser):
+ ref = parser[1]
+ parser.next()
+ # ugh
+ if parser.check('commit'):
+ parse_commit(parser)
+ return
+ if not parser.check('from'):
+ return
+ from_mark = parser.get_mark()
+ parser.next()
+
+ node = parser.repo.changelog.node(mark_to_rev(from_mark))
+ parsed_refs[ref] = node
+
+def parse_tag(parser):
+ name = parser[1]
+ parser.next()
+ from_mark = parser.get_mark()
+ parser.next()
+ tagger = parser.get_author()
+ parser.next()
+ data = parser.get_data()
+ parser.next()
+
+ # nothing to do
+
+def do_export(parser):
+ global parsed_refs, bmarks
+
+ parser.next()
+
+ for line in parser.each_block('done'):
+ if parser.check('blob'):
+ parse_blob(parser)
+ elif parser.check('commit'):
+ parse_commit(parser)
+ elif parser.check('reset'):
+ parse_reset(parser)
+ elif parser.check('tag'):
+ parse_tag(parser)
+ elif parser.check('feature'):
+ pass
+ else:
+ die('unhandled export command: %s' % line)
+
+ for ref, node in parsed_refs.iteritems():
+ if ref.startswith('refs/heads/branches'):
+ pass
+ elif ref.startswith('refs/heads/'):
+ bmark = ref[len('refs/heads/'):]
+ if bmark in bmarks:
+ old = bmarks[bmark].hex()
+ else:
+ old = ''
+ if not bookmarks.pushbookmark(parser.repo, bmark, old, node):
+ continue
+ elif ref.startswith('refs/tags/'):
+ tag = ref[len('refs/tags/'):]
+ parser.repo.tag([tag], node, None, True, None, {})
+ print "ok %s" % ref
+
+ print
+
def main(args):
- global prefix, dirname, marks, branches, bmarks
+ global prefix, dirname, branches, bmarks
+ global marks, blob_marks, parsed_refs
alias = args[1]
url = args[2]
@@ -362,6 +573,8 @@ def main(args):
dirname = os.path.join(gitdir, 'hg', alias)
branches = {}
bmarks = {}
+ blob_marks = {}
+ parsed_refs = {}
repo = get_repo(url, alias)
prefix = 'refs/hg/%s' % alias
--
1.8.0
^ permalink raw reply related
* [PATCH v6 03/16] remote-hg: add support for remote pushing
From: Felipe Contreras @ 2012-11-04 2:13 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Jeff King, Sverre Rabbelier, Johannes Schindelin,
Ilari Liusvaara, Daniel Barkalow, Michael J Gruber,
Felipe Contreras
In-Reply-To: <1351995218-19889-1-git-send-email-felipe.contreras@gmail.com>
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
contrib/remote-helpers/git-remote-hg | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg
index fcceede..45629e0 100755
--- a/contrib/remote-helpers/git-remote-hg
+++ b/contrib/remote-helpers/git-remote-hg
@@ -197,7 +197,7 @@ def fixup_user(user):
return '%s <%s>' % (name, mail)
def get_repo(url, alias):
- global dirname
+ global dirname, peer
myui = ui.ui()
myui.setconfig('ui', 'interactive', 'off')
@@ -526,7 +526,7 @@ def parse_tag(parser):
# nothing to do
def do_export(parser):
- global parsed_refs, bmarks
+ global parsed_refs, bmarks, peer
parser.next()
@@ -562,12 +562,17 @@ def do_export(parser):
print
+ if peer:
+ parser.repo.push(peer, force=False)
+
def main(args):
global prefix, dirname, branches, bmarks
global marks, blob_marks, parsed_refs
+ global peer
alias = args[1]
url = args[2]
+ peer = None
gitdir = os.environ['GIT_DIR']
dirname = os.path.join(gitdir, 'hg', alias)
--
1.8.0
^ permalink raw reply related
* [PATCH v6 01/16] Add new remote-hg transport helper
From: Felipe Contreras @ 2012-11-04 2:13 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Jeff King, Sverre Rabbelier, Johannes Schindelin,
Ilari Liusvaara, Daniel Barkalow, Michael J Gruber,
Felipe Contreras
In-Reply-To: <1351995218-19889-1-git-send-email-felipe.contreras@gmail.com>
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
contrib/remote-helpers/git-remote-hg | 391 +++++++++++++++++++++++++++++++++++
1 file changed, 391 insertions(+)
create mode 100755 contrib/remote-helpers/git-remote-hg
diff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg
new file mode 100755
index 0000000..e37e278
--- /dev/null
+++ b/contrib/remote-helpers/git-remote-hg
@@ -0,0 +1,391 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2012 Felipe Contreras
+#
+
+# Inspired by Rocco Rutte's hg-fast-export
+
+# Just copy to your ~/bin, or anywhere in your $PATH.
+# Then you can clone with:
+# git clone hg::/path/to/mercurial/repo/
+
+from mercurial import hg, ui, bookmarks
+
+import re
+import sys
+import os
+import json
+
+NAME_RE = re.compile('^([^<>]+)')
+AUTHOR_RE = re.compile('^([^<>]+?)? ?<([^<>]+)>$')
+
+def die(msg, *args):
+ sys.stderr.write('ERROR: %s\n' % (msg % args))
+ sys.exit(1)
+
+def warn(msg, *args):
+ sys.stderr.write('WARNING: %s\n' % (msg % args))
+
+def gitmode(flags):
+ return 'l' in flags and '120000' or 'x' in flags and '100755' or '100644'
+
+def gittz(tz):
+ return '%+03d%02d' % (-tz / 3600, -tz % 3600 / 60)
+
+class Marks:
+
+ def __init__(self, path):
+ self.path = path
+ self.tips = {}
+ self.marks = {}
+ self.last_mark = 0
+
+ self.load()
+
+ def load(self):
+ if not os.path.exists(self.path):
+ return
+
+ tmp = json.load(open(self.path))
+
+ self.tips = tmp['tips']
+ self.marks = tmp['marks']
+ self.last_mark = tmp['last-mark']
+
+ def dict(self):
+ return { 'tips': self.tips, 'marks': self.marks, 'last-mark' : self.last_mark }
+
+ def store(self):
+ json.dump(self.dict(), open(self.path, 'w'))
+
+ def __str__(self):
+ return str(self.dict())
+
+ def from_rev(self, rev):
+ return self.marks[str(rev)]
+
+ def get_mark(self, rev):
+ self.last_mark += 1
+ self.marks[str(rev)] = self.last_mark
+ return self.last_mark
+
+ def is_marked(self, rev):
+ return self.marks.has_key(str(rev))
+
+ def get_tip(self, branch):
+ return self.tips.get(branch, 0)
+
+ def set_tip(self, branch, tip):
+ self.tips[branch] = tip
+
+class Parser:
+
+ def __init__(self, repo):
+ self.repo = repo
+ self.line = self.get_line()
+
+ def get_line(self):
+ return sys.stdin.readline().strip()
+
+ def __getitem__(self, i):
+ return self.line.split()[i]
+
+ def check(self, word):
+ return self.line.startswith(word)
+
+ def each_block(self, separator):
+ while self.line != separator:
+ yield self.line
+ self.line = self.get_line()
+
+ def __iter__(self):
+ return self.each_block('')
+
+ def next(self):
+ self.line = self.get_line()
+ if self.line == 'done':
+ self.line = None
+
+def export_file(fc):
+ d = fc.data()
+ print "M %s inline %s" % (gitmode(fc.flags()), fc.path())
+ print "data %d" % len(d)
+ print d
+
+def get_filechanges(repo, ctx, parent):
+ modified = set()
+ added = set()
+ removed = set()
+
+ cur = ctx.manifest()
+ prev = repo[parent].manifest().copy()
+
+ for fn in cur:
+ if fn in prev:
+ if (cur.flags(fn) != prev.flags(fn) or cur[fn] != prev[fn]):
+ modified.add(fn)
+ del prev[fn]
+ else:
+ added.add(fn)
+ removed |= set(prev.keys())
+
+ return added | modified, removed
+
+def fixup_user(user):
+ user = user.replace('"', '')
+ name = mail = None
+ m = AUTHOR_RE.match(user)
+ if m:
+ name = m.group(1)
+ mail = m.group(2).strip()
+ else:
+ m = NAME_RE.match(user)
+ if m:
+ name = m.group(1).strip()
+
+ if not name:
+ name = 'Unknown'
+ if not mail:
+ mail = 'unknown'
+
+ return '%s <%s>' % (name, mail)
+
+def get_repo(url, alias):
+ global dirname
+
+ myui = ui.ui()
+ myui.setconfig('ui', 'interactive', 'off')
+
+ if hg.islocal(url):
+ repo = hg.repository(myui, url)
+ else:
+ local_path = os.path.join(dirname, 'clone')
+ if not os.path.exists(local_path):
+ peer, dstpeer = hg.clone(myui, {}, url, local_path, update=False, pull=True)
+ repo = dstpeer.local()
+ else:
+ repo = hg.repository(myui, local_path)
+ peer = hg.peer(myui, {}, url)
+ repo.pull(peer, heads=None, force=True)
+
+ return repo
+
+def rev_to_mark(rev):
+ global marks
+ return marks.from_rev(rev)
+
+def export_ref(repo, name, kind, head):
+ global prefix, marks
+
+ ename = '%s/%s' % (kind, name)
+ tip = marks.get_tip(ename)
+
+ # mercurial takes too much time checking this
+ if tip and tip == head.rev():
+ # nothing to do
+ return
+ revs = repo.revs('%u:%u' % (tip, head))
+ count = 0
+
+ revs = [rev for rev in revs if not marks.is_marked(rev)]
+
+ for rev in revs:
+
+ c = repo[rev]
+ (manifest, user, (time, tz), files, desc, extra) = repo.changelog.read(c.node())
+ rev_branch = extra['branch']
+
+ author = "%s %d %s" % (fixup_user(user), time, gittz(tz))
+ if 'committer' in extra:
+ user, time, tz = extra['committer'].rsplit(' ', 2)
+ committer = "%s %s %s" % (user, time, gittz(int(tz)))
+ else:
+ committer = author
+
+ parents = [p for p in repo.changelog.parentrevs(rev) if p >= 0]
+
+ if len(parents) == 0:
+ modified = c.manifest().keys()
+ removed = []
+ else:
+ modified, removed = get_filechanges(repo, c, parents[0])
+
+ if len(parents) == 0 and rev:
+ print 'reset %s/%s' % (prefix, ename)
+
+ print "commit %s/%s" % (prefix, ename)
+ print "mark :%d" % (marks.get_mark(rev))
+ print "author %s" % (author)
+ print "committer %s" % (committer)
+ print "data %d" % (len(desc))
+ print desc
+
+ if len(parents) > 0:
+ print "from :%s" % (rev_to_mark(parents[0]))
+ if len(parents) > 1:
+ print "merge :%s" % (rev_to_mark(parents[1]))
+
+ for f in modified:
+ export_file(c.filectx(f))
+ for f in removed:
+ print "D %s" % (f)
+ print
+
+ count += 1
+ if (count % 100 == 0):
+ print "progress revision %d '%s' (%d/%d)" % (rev, name, count, len(revs))
+ print "#############################################################"
+
+ # make sure the ref is updated
+ print "reset %s/%s" % (prefix, ename)
+ print "from :%u" % rev_to_mark(rev)
+ print
+
+ marks.set_tip(ename, rev)
+
+def export_tag(repo, tag):
+ export_ref(repo, tag, 'tags', repo[tag])
+
+def export_bookmark(repo, bmark):
+ head = bmarks[bmark]
+ export_ref(repo, bmark, 'bookmarks', head)
+
+def export_branch(repo, branch):
+ tip = get_branch_tip(repo, branch)
+ head = repo[tip]
+ export_ref(repo, branch, 'branches', head)
+
+def export_head(repo):
+ global g_head
+ export_ref(repo, g_head[0], 'bookmarks', g_head[1])
+
+def do_capabilities(parser):
+ global prefix, dirname
+
+ print "import"
+ print "refspec refs/heads/branches/*:%s/branches/*" % prefix
+ print "refspec refs/heads/*:%s/bookmarks/*" % prefix
+ print "refspec refs/tags/*:%s/tags/*" % prefix
+ print
+
+def get_branch_tip(repo, branch):
+ global branches
+
+ heads = branches.get(branch, None)
+ if not heads:
+ return None
+
+ # verify there's only one head
+ if (len(heads) > 1):
+ warn("Branch '%s' has more than one head, consider merging" % branch)
+ # older versions of mercurial don't have this
+ if hasattr(repo, "branchtip"):
+ return repo.branchtip(branch)
+
+ return heads[0]
+
+def list_head(repo, cur):
+ global g_head
+
+ head = bookmarks.readcurrent(repo)
+ if not head:
+ return
+ node = repo[head]
+ print "@refs/heads/%s HEAD" % head
+ g_head = (head, node)
+
+def do_list(parser):
+ global branches, bmarks
+
+ repo = parser.repo
+ for branch in repo.branchmap():
+ heads = repo.branchheads(branch)
+ if len(heads):
+ branches[branch] = heads
+
+ for bmark, node in bookmarks.listbookmarks(repo).iteritems():
+ bmarks[bmark] = repo[node]
+
+ cur = repo.dirstate.branch()
+
+ list_head(repo, cur)
+ for branch in branches:
+ print "? refs/heads/branches/%s" % branch
+ for bmark in bmarks:
+ print "? refs/heads/%s" % bmark
+
+ for tag, node in repo.tagslist():
+ if tag == 'tip':
+ continue
+ print "? refs/tags/%s" % tag
+
+ print
+
+def do_import(parser):
+ repo = parser.repo
+
+ path = os.path.join(dirname, 'marks-git')
+
+ print "feature done"
+ if os.path.exists(path):
+ print "feature import-marks=%s" % path
+ print "feature export-marks=%s" % path
+ sys.stdout.flush()
+
+ # lets get all the import lines
+ while parser.check('import'):
+ ref = parser[1]
+
+ if (ref == 'HEAD'):
+ export_head(repo)
+ elif ref.startswith('refs/heads/branches/'):
+ branch = ref[len('refs/heads/branches/'):]
+ export_branch(repo, branch)
+ elif ref.startswith('refs/heads/'):
+ bmark = ref[len('refs/heads/'):]
+ export_bookmark(repo, bmark)
+ elif ref.startswith('refs/tags/'):
+ tag = ref[len('refs/tags/'):]
+ export_tag(repo, tag)
+
+ parser.next()
+
+ print 'done'
+
+def main(args):
+ global prefix, dirname, marks, branches, bmarks
+
+ alias = args[1]
+ url = args[2]
+
+ gitdir = os.environ['GIT_DIR']
+ dirname = os.path.join(gitdir, 'hg', alias)
+ branches = {}
+ bmarks = {}
+
+ repo = get_repo(url, alias)
+ prefix = 'refs/hg/%s' % alias
+
+ if not os.path.exists(dirname):
+ os.makedirs(dirname)
+
+ marks_path = os.path.join(dirname, 'marks-hg')
+ marks = Marks(marks_path)
+
+ parser = Parser(repo)
+ for line in parser:
+ if parser.check('capabilities'):
+ do_capabilities(parser)
+ elif parser.check('list'):
+ do_list(parser)
+ elif parser.check('import'):
+ do_import(parser)
+ elif parser.check('export'):
+ do_export(parser)
+ else:
+ die('unhandled command: %s' % line)
+ sys.stdout.flush()
+
+ marks.store()
+
+sys.exit(main(sys.argv))
--
1.8.0
^ permalink raw reply related
* [PATCH v6 00/16] New remote-hg helper:w
From: Felipe Contreras @ 2012-11-04 2:13 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Jeff King, Sverre Rabbelier, Johannes Schindelin,
Ilari Liusvaara, Daniel Barkalow, Michael J Gruber,
Felipe Contreras
Hi,
Only a few updates, and has been moved to contrib/remote-helpers
(git-remote-bzr is on the way).
This remote-hg has advantages other tools don't have:
* Uses transport-helper (git clone hg::path)
* The code is small
* The code is simple
* No external dependencies (other than mercurial)
* It's easy to install (put into your path)
* Has extensive tests (for real)
* Active development
* Has compatibility with hg-git
* The required patches are available
* No changes necesary to git core
* Support for bookmarks
* Support for tags
One important alternative is the one written by Sverre Rabbelier that is
now maintained and distributed in msysgit. A list of issues with that
approach (not exhaustive):
* Doesn't work on newer versions of mercurial
* There are multiple versions, each with different issues
* Don't pass a single of this remote-hg's tests
To use it add it to your $PATH (e.g. ~/bin).
% git clone hd:///full/path/or/url/to/hg/repo
To run the tests:
% make -C contrib/remote-helpers test
The only caveat is that you need 'python' in your $PATH.
Changes since v5:
* Move to contrib/remote-helpers
* Reorganize tests
* Fix update of bookmarks
* Fix for older versions of python
* Performance improvements
* Improve default branch/bookmark handling
* Add fixes test-lib
* Cleanups
Changes since v4:
Felipe Contreras (16):
Add new remote-hg transport helper
remote-hg: add support for pushing
remote-hg: add support for remote pushing
remote-hg: add support to push URLs
remote-hg: make sure the encoding is correct
remote-hg: match hg merge behavior
remote-hg: add support for hg-git compat mode
remote-hg: add compat for hg-git author fixes
remote-hg: fake bookmark when there's none
remote-hg: add basic tests
test-lib: avoid full path to store test results
remote-hg: add bidirectional tests
remote-hg: add tests to compare with hg-git
remote-hg: add extra author test
remote-hg: add option to not track branches
remote-hg: the author email can be null
contrib/remote-helpers/Makefile | 13 +
contrib/remote-helpers/git-remote-hg | 785 +++++++++++++++++++++++++++++++
contrib/remote-helpers/test-hg-bidi.sh | 243 ++++++++++
contrib/remote-helpers/test-hg-hg-git.sh | 466 ++++++++++++++++++
contrib/remote-helpers/test-hg.sh | 112 +++++
t/test-lib.sh | 3 +-
6 files changed, 1621 insertions(+), 1 deletion(-)
create mode 100644 contrib/remote-helpers/Makefile
create mode 100755 contrib/remote-helpers/git-remote-hg
create mode 100755 contrib/remote-helpers/test-hg-bidi.sh
create mode 100755 contrib/remote-helpers/test-hg-hg-git.sh
create mode 100755 contrib/remote-helpers/test-hg.sh
--
1.8.0
^ permalink raw reply
* Re: git-p4 clone @all error
From: Pete Wyckoff @ 2012-11-03 23:13 UTC (permalink / raw)
To: Thomas Berg; +Cc: Arthur, git
In-Reply-To: <CABYiQp=1HEW=53U2Rck5vckhq0PB3C9iuanoXeVvNG6Xv5+oHg@mail.gmail.com>
merlin66b@gmail.com wrote on Wed, 31 Oct 2012 15:01 +0100:
> On Tue, Oct 30, 2012 at 11:44 AM, Arthur <a.foulon@amesys.fr> wrote:
> > The problem :
> >
> > Importing revision 7727 (100%)Traceback (most recent call last):
> > File "/usr/bin/git-p4", line 3183, in <module>
> > main()
> > File "/usr/bin/git-p4", line 3177, in main
> > if not cmd.run(args):
> > File "/usr/bin/git-p4", line 3048, in run
> > if not P4Sync.run(self, depotPaths):
> > File "/usr/bin/git-p4", line 2911, in run
> > self.importChanges(changes)
> > File "/usr/bin/git-p4", line 2618, in importChanges
> > self.initialParent)
> > File "/usr/bin/git-p4", line 2198, in commit
> > epoch = details["time"]
> > KeyError: 'time'
>
> Are you permanently converting a project, or are you planning to
> continue submitting to perforce with git-p4?
>
> I have seen similar bugs myself when using the --detect-branches
> option.
I hope --detect-branches is unrelated to any problems related to
p4 describe, because it is used identically both with and without
the option. But you never know.
> The branch detection in git-p4 is flaky anyway: it is limited
> what it can handle, and it used to require correct perforce branch
> specs at least, so I would recommend not using it unless you know what
> it is doing under the hood.
It relies on heuristics because the git idea of branches doesn't
have a direct analog in p4, even though p4 users will organize
their files and directories in ways that suggest branches. The
way it works is described in the BRANCH DETECTION part of the
git-p4.1 man page.
The man page also explains how you can use a config variable
to explicitly define the branch relationships. This might help:
git-p4.branchList::
List of branches to be imported when branch detection is
enabled. Each entry should be a pair of branch names separated
by a colon (:). This example declares that both branchA and
branchB were created from main:
git config git-p4.branchList main:branchA
git config --add git-p4.branchList main:branchB
It still only works for branches at the same "level" of the
depot path.
> Instead I would just clone a single branch at a time (drop the
> --detect-branches) and work on that.
>
> I do this even in the rare cases when I need more than one perforce
> branch in the same git repo - there are other ways to achieve the same
> thing.
Yep, that should alway works.
-- Pete
^ permalink raw reply
* Re: [PATCH] git p4: catch p4 describe errors
From: Matt Arsenault @ 2012-11-03 23:12 UTC (permalink / raw)
To: Pete Wyckoff; +Cc: git, Arthur
In-Reply-To: <20121103230701.GA11267@padd.com>
On Nov 3, 2012, at 16:07 , Pete Wyckoff <pw@padd.com> wrote:
>
> Arthur and Matt, you've both had intermittent "p4 describe"
> errors. I've not been able to repeat this or come up with
> a possible root cause. But it is clear that the error handling
> in this area is weak.
I tried this week to find the commit that caused the problem, but there were too many to sift through so I haven't managed to see it again either
^ permalink raw reply
* [PATCH] git p4: catch p4 describe errors
From: Pete Wyckoff @ 2012-11-03 23:07 UTC (permalink / raw)
To: git; +Cc: Arthur, Matt Arsenault
In-Reply-To: <20121103140946.GB4651@padd.com>
Group the two calls to "p4 describe" into a new helper function,
and try to validate the p4 results. The current behavior when p4
describe fails is to die with a python backtrace. The new behavior
will print the full response.
Based-on-patch-by: Matt Arsenault <arsenm2@gmail.com>
Signed-off-by: Pete Wyckoff <pw@padd.com>
---
Arthur and Matt, you've both had intermittent "p4 describe"
errors. I've not been able to repeat this or come up with
a possible root cause. But it is clear that the error handling
in this area is weak.
Can you continue using git-p4 with this patch applied? If
it fails again, at least we'll get some interesting output.
This is appropriate for upstream too, since it should do no harm
and might flush out a bug at some point. It includes "-s" on
both p4 describe calls now as Matt suggested.
-- Pete
git-p4.py | 35 ++++++++++++++++++++++++++---------
1 file changed, 26 insertions(+), 9 deletions(-)
diff --git a/git-p4.py b/git-p4.py
index 882b1bb..e51a081 100755
--- a/git-p4.py
+++ b/git-p4.py
@@ -169,6 +169,29 @@ def p4_reopen(type, f):
def p4_move(src, dest):
p4_system(["move", "-k", wildcard_encode(src), wildcard_encode(dest)])
+def p4_describe(change):
+ """Make sure it returns a valid result by checking for
+ the presence of field "time". Return a dict of the
+ results."""
+
+ ds = p4CmdList(["describe", "-s", str(change)])
+ if len(ds) != 1:
+ die("p4 describe -s %d did not return 1 result: %s" % (change, str(ds)))
+
+ d = ds[0]
+
+ if "p4ExitCode" in d:
+ die("p4 describe -s %d exited with %d: %s" % (change, d["p4ExitCode"],
+ str(d)))
+ if "code" in d:
+ if d["code"] == "error":
+ die("p4 describe -s %d returned error code: %s" % (change, str(d)))
+
+ if "time" not in d:
+ die("p4 describe -s %d returned no \"time\": %s" % (change, str(d)))
+
+ return d
+
#
# Canonicalize the p4 type and return a tuple of the
# base type, plus any modifiers. See "p4 help filetypes"
@@ -2543,7 +2566,7 @@ class P4Sync(Command, P4UserMap):
def importChanges(self, changes):
cnt = 1
for change in changes:
- description = p4Cmd(["describe", str(change)])
+ description = p4_describe(change)
self.updateOptionDict(description)
if not self.silent:
@@ -2667,14 +2690,8 @@ class P4Sync(Command, P4UserMap):
# Use time from top-most change so that all git p4 clones of
# the same p4 repo have the same commit SHA1s.
- res = p4CmdList("describe -s %d" % newestRevision)
- newestTime = None
- for r in res:
- if r.has_key('time'):
- newestTime = int(r['time'])
- if newestTime is None:
- die("\"describe -s\" on newest change %d did not give a time")
- details["time"] = newestTime
+ res = p4_describe(newestRevision)
+ details["time"] = res["time"]
self.updateOptionDict(details)
try:
--
1.7.12.1.457.g468b3ef
^ permalink raw reply related
* Re: [PATCH] Enable parallelism in git submodule update.
From: Jens Lehmann @ 2012-11-03 19:13 UTC (permalink / raw)
To: Phil Hord; +Cc: Stefan Zager, git, Heiko Voigt, Junio C Hamano
In-Reply-To: <CABURp0pkv714k_+S2seTtdHMNJFzkgijYuNuWcfNvnF+c21cDg@mail.gmail.com>
Am 03.11.2012 19:44, schrieb Phil Hord:
> On Sat, Nov 3, 2012 at 11:42 AM, Jens Lehmann <Jens.Lehmann@web.de> wrote:
>> Am 30.10.2012 19:11, schrieb Stefan Zager:
>>> This is a refresh of a conversation from a couple of months ago.
>>>
>>> I didn't try to implement all the desired features (e.g., smart logic
>>> for passing a -j parameter to recursive submodule invocations), but I
>>> did address the one issue that Junio insisted on: the code makes a
>>> best effort to detect whether xargs supports parallel execution on the
>>> host platform, and if it doesn't, then it prints a warning and falls
>>> back to serial execution.
>>
>> I suspect not passing on --jobs recursively like you do here is the
>> right thing to do, as that would give exponential growth of jobs with
>> recursion depth, which makes no sense to me.
>
> On the other hand, since $jobs is still defined when the recursive
> call to is made to 'eval cmd_update "$orig_flags"', I suspect the
> value *is* passed down recursively.
But for $jobs != 1 Stefan's code doesn't use eval cmd_update but
starts the submodule script again:
+ xargs $max_lines -P "$jobs" git submodule update $orig_flags
That should get rid of the $jobs setting, or am I missing something?
> Maybe $jobs should be manually
> reset before recursing -- unless it is "0" -- though I expect someone
> would feel differently if she had one submodule on level 1 and 10
> submodules on level 2. She would be surprised, then, when --jobs=10
> seemed to have little affect on performance.
Hmm, good point. However we implement that, it should at least be
properly documented in the man page (and in the use case you describe
a "git submodule foreach 'git submodule update -j 10'" could be the
solution if we choose to not propagate the jobs option).
> So maybe it is best to
> leave it as it is, excepting that the apparent attempt not to pass the
> switch down is probably misleading.
I didn't test it, but I think it should work (famous last words ;-).
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox