* [PATCH v5 13/14] remote-hg: add tests to compare with hg-git
From: Felipe Contreras @ 2012-10-30 4:35 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Sverre Rabbelier, Johannes Schindelin,
Ilari Liusvaara, Daniel Barkalow, Jeff King, Michael J Gruber,
Felipe Contreras
In-Reply-To: <1351571736-4682-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-hg/test-hg-git.sh | 460 +++++++++++++++++++++++++++++++++++++++
1 file changed, 460 insertions(+)
create mode 100755 contrib/remote-hg/test-hg-git.sh
diff --git a/contrib/remote-hg/test-hg-git.sh b/contrib/remote-hg/test-hg-git.sh
new file mode 100755
index 0000000..2b7acb0
--- /dev/null
+++ b/contrib/remote-hg/test-hg-git.sh
@@ -0,0 +1,460 @@
+#!/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
+}
+
+test_expect_success '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"
+'
+
+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 v5 14/14] remote-hg: add extra author test
From: Felipe Contreras @ 2012-10-30 4:35 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Sverre Rabbelier, Johannes Schindelin,
Ilari Liusvaara, Daniel Barkalow, Jeff King, Michael J Gruber,
Felipe Contreras
In-Reply-To: <1351571736-4682-1-git-send-email-felipe.contreras@gmail.com>
For hg.hg.
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
contrib/remote-hg/test-hg-git.sh | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/contrib/remote-hg/test-hg-git.sh b/contrib/remote-hg/test-hg-git.sh
index 2b7acb0..a9f5cb2 100755
--- a/contrib/remote-hg/test-hg-git.sh
+++ b/contrib/remote-hg/test-hg-git.sh
@@ -368,7 +368,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 v5 12/14] remote-hg: add biridectional tests
From: Felipe Contreras @ 2012-10-30 4:35 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Sverre Rabbelier, Johannes Schindelin,
Ilari Liusvaara, Daniel Barkalow, Jeff King, Michael J Gruber,
Felipe Contreras
In-Reply-To: <1351571736-4682-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-hg/Makefile | 13 +++
contrib/remote-hg/test.sh | 241 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 254 insertions(+)
create mode 100644 contrib/remote-hg/Makefile
create mode 100755 contrib/remote-hg/test.sh
diff --git a/contrib/remote-hg/Makefile b/contrib/remote-hg/Makefile
new file mode 100644
index 0000000..9a76575
--- /dev/null
+++ b/contrib/remote-hg/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-hg/test.sh b/contrib/remote-hg/test.sh
new file mode 100755
index 0000000..4ea2b24
--- /dev/null
+++ b/contrib/remote-hg/test.sh
@@ -0,0 +1,241 @@
+#!/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
+
+# 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/'
+}
+
+test_expect_success '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"
+'
+
+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 v5 10/14] remote-hg: fake bookmark when there's none
From: Felipe Contreras @ 2012-10-30 4:35 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Sverre Rabbelier, Johannes Schindelin,
Ilari Liusvaara, Daniel Barkalow, Jeff King, Michael J Gruber,
Felipe Contreras
In-Reply-To: <1351571736-4682-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-hg/git-remote-hg | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/contrib/remote-hg/git-remote-hg b/contrib/remote-hg/git-remote-hg
index c2efadf..c41ec95 100755
--- a/contrib/remote-hg/git-remote-hg
+++ b/contrib/remote-hg/git-remote-hg
@@ -422,12 +422,20 @@ def list_branch_head(repo, cur):
g_head = (head, 'branches', repo[tip])
def list_bookmark_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
+ tip = get_branch_tip(repo, head)
+ if not tip:
+ return
+ node = repo[tip]
+ bmarks[head] = node
+
print "@refs/heads/%s HEAD" % head
g_head = (head, 'bookmarks', node)
--
1.8.0
^ permalink raw reply related
* [PATCH v5 11/14] remote-hg: add support for fake remote
From: Felipe Contreras @ 2012-10-30 4:35 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Sverre Rabbelier, Johannes Schindelin,
Ilari Liusvaara, Daniel Barkalow, Jeff King, Michael J Gruber,
Felipe Contreras
In-Reply-To: <1351571736-4682-1-git-send-email-felipe.contreras@gmail.com>
Helpful while testing.
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
contrib/remote-hg/git-remote-hg | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/contrib/remote-hg/git-remote-hg b/contrib/remote-hg/git-remote-hg
index c41ec95..61c1072 100755
--- a/contrib/remote-hg/git-remote-hg
+++ b/contrib/remote-hg/git-remote-hg
@@ -248,7 +248,13 @@ def get_repo(url, alias):
myui = ui.ui()
myui.setconfig('ui', 'interactive', 'off')
- if hg.islocal(url):
+ if url.startswith("remote://"):
+ remote = True
+ url = "file://%s" % url[9:]
+ else:
+ remote = False
+
+ if hg.islocal(url) and not remote:
repo = hg.repository(myui, url)
else:
local_path = os.path.join(dirname, 'clone')
--
1.8.0
^ permalink raw reply related
* [PATCH v5 09/14] remote-hg: add compat for hg-git author fixes
From: Felipe Contreras @ 2012-10-30 4:35 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Sverre Rabbelier, Johannes Schindelin,
Ilari Liusvaara, Daniel Barkalow, Jeff King, Michael J Gruber,
Felipe Contreras
In-Reply-To: <1351571736-4682-1-git-send-email-felipe.contreras@gmail.com>
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
contrib/remote-hg/git-remote-hg | 59 ++++++++++++++++++++++++++++++++++++-----
1 file changed, 53 insertions(+), 6 deletions(-)
diff --git a/contrib/remote-hg/git-remote-hg b/contrib/remote-hg/git-remote-hg
index e49f9ed..c2efadf 100755
--- a/contrib/remote-hg/git-remote-hg
+++ b/contrib/remote-hg/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)
@@ -180,9 +193,9 @@ def get_filechanges(repo, ctx, parents):
changed, added, removed = [set(sum(e, [])) for e in zip(*l)]
return added | changed, 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)
@@ -191,11 +204,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)
@@ -649,7 +692,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]
@@ -665,8 +708,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 v5 08/14] remote-hg: add support for hg-git compat mode
From: Felipe Contreras @ 2012-10-30 4:35 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Sverre Rabbelier, Johannes Schindelin,
Ilari Liusvaara, Daniel Barkalow, Jeff King, Michael J Gruber,
Felipe Contreras
In-Reply-To: <1351571736-4682-1-git-send-email-felipe.contreras@gmail.com>
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
contrib/remote-hg/git-remote-hg | 102 +++++++++++++++++++++++++++++++++++++---
1 file changed, 95 insertions(+), 7 deletions(-)
diff --git a/contrib/remote-hg/git-remote-hg b/contrib/remote-hg/git-remote-hg
index c28f4b4..e49f9ed 100755
--- a/contrib/remote-hg/git-remote-hg
+++ b/contrib/remote-hg/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('^([^<>]+?)? ?<([^<>]+)>$')
@@ -212,7 +228,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)
@@ -247,6 +263,33 @@ def export_ref(repo, name, kind, head):
else:
modified, removed = get_filechanges(repo, c, parents)
+ 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)
@@ -335,8 +378,18 @@ def list_branch_head(repo, cur):
print "@refs/heads/%s HEAD" % head
g_head = (head, 'branches', repo[tip])
+def list_bookmark_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, 'bookmarks', node)
+
def do_list(parser):
- global branches, bmarks
+ global branches, bmarks, mode
repo = parser.repo
for branch in repo.branchmap():
@@ -349,9 +402,13 @@ def do_list(parser):
cur = repo.dirstate.branch()
- list_branch_head(repo, cur)
- for branch in branches:
- print "? refs/heads/branches/%s" % branch
+ if mode != 'hg':
+ list_branch_head(repo, cur)
+ for branch in branches:
+ print "? refs/heads/branches/%s" % branch
+ else:
+ list_bookmark_head(repo, cur)
+
for bmark in bmarks:
print "? refs/heads/%s" % bmark
@@ -417,6 +474,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
@@ -463,7 +521,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
@@ -490,6 +550,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)
@@ -574,12 +649,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 v5 07/14] remote-hg: match hg merge behavior
From: Felipe Contreras @ 2012-10-30 4:35 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Sverre Rabbelier, Johannes Schindelin,
Ilari Liusvaara, Daniel Barkalow, Jeff King, Michael J Gruber,
Felipe Contreras
In-Reply-To: <1351571736-4682-1-git-send-email-felipe.contreras@gmail.com>
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
contrib/remote-hg/git-remote-hg | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/contrib/remote-hg/git-remote-hg b/contrib/remote-hg/git-remote-hg
index 29824be..c28f4b4 100755
--- a/contrib/remote-hg/git-remote-hg
+++ b/contrib/remote-hg/git-remote-hg
@@ -409,6 +409,12 @@ 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:
+ f = { 'ctx' : repo[p1][e] }
+ files[e] = f
+
def parse_commit(parser):
global marks, blob_marks, bmarks, parsed_refs
@@ -453,6 +459,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)
@@ -475,6 +483,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 v5 06/14] remote-hg: make sure the encoding is correct
From: Felipe Contreras @ 2012-10-30 4:35 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Sverre Rabbelier, Johannes Schindelin,
Ilari Liusvaara, Daniel Barkalow, Jeff King, Michael J Gruber,
Felipe Contreras
In-Reply-To: <1351571736-4682-1-git-send-email-felipe.contreras@gmail.com>
Independently of the environment.
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
contrib/remote-hg/git-remote-hg | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/contrib/remote-hg/git-remote-hg b/contrib/remote-hg/git-remote-hg
index 4d49923..29824be 100755
--- a/contrib/remote-hg/git-remote-hg
+++ b/contrib/remote-hg/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
@@ -373,6 +373,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]
@@ -391,6 +394,8 @@ def do_import(parser):
parser.next()
+ encoding.encoding = tmp
+
print 'done'
def parse_blob(parser):
@@ -474,8 +479,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 v5 05/14] remote-hg: add support to push URLs
From: Felipe Contreras @ 2012-10-30 4:35 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Sverre Rabbelier, Johannes Schindelin,
Ilari Liusvaara, Daniel Barkalow, Jeff King, Michael J Gruber,
Felipe Contreras
In-Reply-To: <1351571736-4682-1-git-send-email-felipe.contreras@gmail.com>
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
contrib/remote-hg/git-remote-hg | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/contrib/remote-hg/git-remote-hg b/contrib/remote-hg/git-remote-hg
index 959ab80..4d49923 100755
--- a/contrib/remote-hg/git-remote-hg
+++ b/contrib/remote-hg/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('^([^<>]+?)? ?<([^<>]+)>$')
@@ -554,6 +555,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 = {}
@@ -584,6 +591,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 v5 04/14] remote-hg: add support for remote pushing
From: Felipe Contreras @ 2012-10-30 4:35 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Sverre Rabbelier, Johannes Schindelin,
Ilari Liusvaara, Daniel Barkalow, Jeff King, Michael J Gruber,
Felipe Contreras
In-Reply-To: <1351571736-4682-1-git-send-email-felipe.contreras@gmail.com>
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
contrib/remote-hg/git-remote-hg | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/contrib/remote-hg/git-remote-hg b/contrib/remote-hg/git-remote-hg
index 337ba40..959ab80 100755
--- a/contrib/remote-hg/git-remote-hg
+++ b/contrib/remote-hg/git-remote-hg
@@ -183,7 +183,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')
@@ -511,7 +511,7 @@ def parse_tag(parser):
# nothing to do
def do_export(parser):
- global parsed_refs
+ global parsed_refs, peer
parser.next()
@@ -542,12 +542,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 v5 03/14] remote-hg: add support for pushing
From: Felipe Contreras @ 2012-10-30 4:35 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Sverre Rabbelier, Johannes Schindelin,
Ilari Liusvaara, Daniel Barkalow, Jeff King, Michael J Gruber,
Felipe Contreras
In-Reply-To: <1351571736-4682-1-git-send-email-felipe.contreras@gmail.com>
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
contrib/remote-hg/git-remote-hg | 215 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 213 insertions(+), 2 deletions(-)
diff --git a/contrib/remote-hg/git-remote-hg b/contrib/remote-hg/git-remote-hg
index 9e29daa..337ba40 100755
--- a/contrib/remote-hg/git-remote-hg
+++ b/contrib/remote-hg/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 next_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())
@@ -160,6 +206,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
@@ -249,9 +299,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):
@@ -334,8 +392,159 @@ 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
+
+ a = parser.line.split(' ')
+ ref = a[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):
+ a = parser.line.split(' ')
+ ref = a[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):
+ a = parser.line.split(' ')
+ name = a[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
+
+ 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/'):]
+ bookmarks.pushbookmark(parser.repo, bmark, '', node)
+ 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]
@@ -344,6 +553,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 v5 01/14] Add new remote-hg transport helper
From: Felipe Contreras @ 2012-10-30 4:35 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Sverre Rabbelier, Johannes Schindelin,
Ilari Liusvaara, Daniel Barkalow, Jeff King, Michael J Gruber,
Felipe Contreras
In-Reply-To: <1351571736-4682-1-git-send-email-felipe.contreras@gmail.com>
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
contrib/remote-hg/git-remote-hg | 359 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 359 insertions(+)
create mode 100755 contrib/remote-hg/git-remote-hg
diff --git a/contrib/remote-hg/git-remote-hg b/contrib/remote-hg/git-remote-hg
new file mode 100755
index 0000000..67d39fa
--- /dev/null
+++ b/contrib/remote-hg/git-remote-hg
@@ -0,0 +1,359 @@
+#!/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
+
+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 next_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, parents):
+ l = [repo.status(p, ctx)[:3] for p in parents]
+ changed, added, removed = [set(sum(e, [])) for e in zip(*l)]
+ return added | changed, 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)
+
+ if len(parents) == 0 and rev:
+ print 'reset %s/%s' % (prefix, ename)
+
+ print "commit %s/%s" % (prefix, ename)
+ print "mark :%d" % (marks.next_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 removed:
+ print "D %s" % (f)
+ for f in modified:
+ export_file(c.filectx(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_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], g_head[1], g_head[2])
+
+def do_capabilities(parser):
+ global prefix, dirname
+
+ print "import"
+ print "refspec refs/heads/branches/*:%s/branches/*" % 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)
+ return repo.branchtip(branch)
+
+ return heads[0]
+
+def list_branch_head(repo, cur):
+ global g_head
+
+ tip = get_branch_tip(repo, cur)
+ head = 'branches/' + cur
+ print "@refs/heads/%s HEAD" % head
+ g_head = (head, 'branches', repo[tip])
+
+def do_list(parser):
+ global branches
+
+ repo = parser.repo
+ for branch in repo.branchmap():
+ heads = repo.branchheads(branch)
+ if len(heads):
+ branches[branch] = heads
+
+ cur = repo.dirstate.branch()
+
+ list_branch_head(repo, cur)
+ for branch in branches:
+ print "? refs/heads/branches/%s" % branch
+
+ 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/tags/'):
+ tag = ref[len('refs/tags/'):]
+ export_tag(repo, tag)
+
+ parser.next()
+
+ print 'done'
+
+def main(args):
+ global prefix, dirname, marks, branches
+
+ alias = args[1]
+ url = args[2]
+
+ gitdir = os.environ['GIT_DIR']
+ dirname = os.path.join(gitdir, 'hg', alias)
+ branches = {}
+
+ 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 v5 02/14] remote-hg: add support for bookmarks
From: Felipe Contreras @ 2012-10-30 4:35 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Sverre Rabbelier, Johannes Schindelin,
Ilari Liusvaara, Daniel Barkalow, Jeff King, Michael J Gruber,
Felipe Contreras
In-Reply-To: <1351571736-4682-1-git-send-email-felipe.contreras@gmail.com>
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
contrib/remote-hg/git-remote-hg | 20 +++++++++++++++++---
1 file changed, 17 insertions(+), 3 deletions(-)
diff --git a/contrib/remote-hg/git-remote-hg b/contrib/remote-hg/git-remote-hg
index 67d39fa..9e29daa 100755
--- a/contrib/remote-hg/git-remote-hg
+++ b/contrib/remote-hg/git-remote-hg
@@ -9,7 +9,7 @@
# Then you can clone with:
# git clone hg::/path/to/mercurial/repo/
-from mercurial import hg, ui
+from mercurial import hg, ui, bookmarks
import re
import sys
@@ -232,6 +232,10 @@ def export_ref(repo, name, kind, head):
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]
@@ -246,6 +250,7 @@ def do_capabilities(parser):
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
@@ -272,7 +277,7 @@ def list_branch_head(repo, cur):
g_head = (head, 'branches', repo[tip])
def do_list(parser):
- global branches
+ global branches, bmarks
repo = parser.repo
for branch in repo.branchmap():
@@ -280,11 +285,16 @@ def do_list(parser):
if len(heads):
branches[branch] = heads
+ for bmark, node in bookmarks.listbookmarks(repo).iteritems():
+ bmarks[bmark] = repo[node]
+
cur = repo.dirstate.branch()
list_branch_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':
@@ -313,6 +323,9 @@ def do_import(parser):
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)
@@ -322,7 +335,7 @@ def do_import(parser):
print 'done'
def main(args):
- global prefix, dirname, marks, branches
+ global prefix, dirname, marks, branches, bmarks
alias = args[1]
url = args[2]
@@ -330,6 +343,7 @@ def main(args):
gitdir = os.environ['GIT_DIR']
dirname = os.path.join(gitdir, 'hg', alias)
branches = {}
+ bmarks = {}
repo = get_repo(url, alias)
prefix = 'refs/hg/%s' % alias
--
1.8.0
^ permalink raw reply related
* [PATCH v5 00/14] New remote-hg helper
From: Felipe Contreras @ 2012-10-30 4:35 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Sverre Rabbelier, Johannes Schindelin,
Ilari Liusvaara, Daniel Barkalow, Jeff King, Michael J Gruber,
Felipe Contreras
Hi,
I've ported the tests from hg-git and made sure that the output from remote-hg
matches the output of hg-git. With these extensive tests I would consider this
one ready for wide use. Not only do the tests pass, I've compared the generated
repos of a few projects, and the SHA-1's are exactly the same :)
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
* Active development
* Has compatibility with hg-git
* The required patches are available
* No changes necesary to git core
One important alternative is the one written by Sverre Rabbelier that is now
maintained and distributed in msysgit. It's hard to evaluate this option as
there isn't a branch specific to this remote helper so it would be possible to
evaluate the necessary patches.
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-hg tests
The only caveat is that you need 'python' in your $PATH.
Changes since v4:
* Add new simple biridectional tests (no need for hg-git)
* Move tests to contrib directory
* Add dependency checks to the tests
* Trivial fixes
Changes since v3:
* New extensive tests
* Add compatibility mode with hg-git
* Added support for boomkars
* Add mercurial information to the git msg (branch, renames, extra, etc.)
Changes since v2:
* Added support for pushing
* Tests copied from original remote-hg
* Custom default -> master renames removed
* Code reorganized
Changes since v1:
* Improved documentation
* Use more common 'python' binary
* Warn, don't barf when a branch has multiple heads
* Fixed marks to fetch after cloned
* Support for cloning/pulling remote repositories
* Use a more appropriate internal directory (e.g. .git/hg/origin)
* Fixes for python3
Felipe Contreras (14):
Add new remote-hg transport helper
remote-hg: add support for bookmarks
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 support for fake remote
remote-hg: add biridectional tests
remote-hg: add tests to compare with hg-git
remote-hg: add extra author test
contrib/remote-hg/Makefile | 13 +
contrib/remote-hg/git-remote-hg | 773 +++++++++++++++++++++++++++++++++++++++
contrib/remote-hg/test-hg-git.sh | 464 +++++++++++++++++++++++
contrib/remote-hg/test.sh | 241 ++++++++++++
4 files changed, 1491 insertions(+)
create mode 100644 contrib/remote-hg/Makefile
create mode 100755 contrib/remote-hg/git-remote-hg
create mode 100755 contrib/remote-hg/test-hg-git.sh
create mode 100755 contrib/remote-hg/test.sh
--
1.8.0
^ permalink raw reply
* Re: [PATCH] test-lib: avoid full path to store test results
From: Jeff King @ 2012-10-30 4:28 UTC (permalink / raw)
To: Felipe Contreras
Cc: git, Junio C Hamano, Jonathan Nieder,
Ævar Arnfjörð Bjarmason, Johannes Sixt
In-Reply-To: <1351570377-894-1-git-send-email-felipe.contreras@gmail.com>
On Tue, Oct 30, 2012 at 05:12:57AM +0100, Felipe Contreras wrote:
> No reason to use the full path in case this is used externally.
I think it is not just "no reason to", but it is actively wrong to use a
full path, as we do not take care to "mkdir -p" the intervening path
components.
However, this never comes up in practice, because all of the test
scripts assume you are running them from the test directory (i.e.,
they will fail otherwise because they will not find ./test-lib.sh).
Is this in support of putting remote-hg tests in contrib/? I had
expected you to just put
export TEST_DIRECTORY="$(pwd)/../../../t"
. "$TEST_DIRECTORY/test-lib.sh"
into the test script in contrib/remote-hg/t. I guess you are doing
something like:
cd ../../../t && ../contrib/remote-hg/t/twhatever...
but the former seems much simpler to invoke (and if the goal is to get
your test-results in the right place, setting TEST_OUTPUT_DIRECTORY is
the best way to do that).
If this is part of the remote-hg series, I'd prefer to just see it as
part of the re-roll. It's much easier to evaluate it in context.
Or are you really just doing:
cd git/t
$PWD/t0000-basic.sh
I guess there is nothing wrong with that, though there is no reason not
to use "./" instead of $PWD.
-Peff
^ permalink raw reply
* [PATCH] test-lib: avoid full path to store test results
From: Felipe Contreras @ 2012-10-30 4:12 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Jeff King, Jonathan Nieder,
Ævar Arnfjörð Bjarmason, Johannes Sixt,
Felipe Contreras
No reason to use the full path in case this is used externally.
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
* Empty name in fast-import stream.
From: Alexander Usov @ 2012-10-30 0:16 UTC (permalink / raw)
To: git
Hi,
I'm working now on converting large repo to git, and stumbled upon a
case where fast-import from old git creates incorrect commit objects.
The case is following: when exporting the commit where the only thing
I know about committer is email, fast-import spec dictates following
format:
committer <foo@bar.com> 123456789 +100
with just 1 space between committer and opening brace. Apparently
git-fast-import in 1.7.1 would produce a commit object with just 1
space in there too. And git-fsck in newer versions of git would
complain about this commit object.
The workaround I found is to emit 2 spaces between committer and
opening bracket. Such stream is accepted by both 1.7.1 and 1.8 and
they both produce correct commit objects.
I'm wondering if it is worth changing git-fast-import manual page to
have following rule for committer (and author):
('author' SP <name>? SP LT <email> GT SP <when> LF)?
'committer' SP <name>? SP LT <email> GT SP <when> LF
This format results in the fast-import streams which are correctly
handled by both old and newer git versions.
--
Best regards,
Alexander.
^ permalink raw reply
* [PATCHv2 3/3] git-status: show short sequencer state
From: Phil Hord @ 2012-10-29 23:31 UTC (permalink / raw)
To: git
Cc: phil.hord, Jeff King, Junio C Hamano, konglu, Matthieu Moy,
Kong Lucien, Duperray Valentin, Jonas Franck, Nguy Thomas,
Phil Hord
In-Reply-To: <1351553513-20385-1-git-send-email-hordp@cisco.com>
Teach git-status to report the sequencer state in short form
using a new --sequencer (-S) switch. Output zero or more
simple state token strings indicating the deduced state of the
git sequencer.
Sequencer state info tokens are displayed in "short"
form. 'git status --short -S' will show the normal short
status including the sequencer tokens. 'git status -S'
without --short will show only the new sequencer information
tokens in whatever the requested format is.
State token strings which may be emitted and their meanings:
merge a merge is in progress
am an am is in progress
am-is-empty the am patch is empty
rebase a rebase is in progress
rebase-interactive an interactive rebase is in progress
cherry-pick a cherry-pick is in progress
bisect a bisect is in progress
conflicted there are unresolved conflicts
commit-pending a commit operation is waiting to be completed
splitting interactive rebase, commit is being split
I also considered adding these tokens, but I decided it was not
appropriate since these changes are not sequencer-related. But
it is possible I am being too short-sighted or have chosen the
switch name poorly.
changed-index Changes exist in the index
changed-files Changes exist in the working directory
untracked New files exist in the working directory
Signed-off-by: Phil Hord <hordp@cisco.com>
---
Documentation/git-status.txt | 16 ++++++++++++++++
builtin/commit.c | 14 ++++++++++++++
wt-status.c | 35 +++++++++++++++++++++++++++++++++++
wt-status.h | 7 +++++++
4 files changed, 72 insertions(+)
diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt
index 67e5f53..7a699f1 100644
--- a/Documentation/git-status.txt
+++ b/Documentation/git-status.txt
@@ -38,6 +38,22 @@ OPTIONS
across git versions and regardless of user configuration. See
below for details.
+-S::
+--sequence::
+ Show the git sequencer status. This shows zero or more strings
+ describing the state of the git sequencer.
++
+ merge a merge is in progress
+ am an am is in progress
+ am-is-empty the am patch is empty
+ rebase a rebase is in progress
+ rebase-interactive an interactive rebase is in progress
+ cherry-pick a cherry-pick is in progress
+ bisect a bisect is in progress
+ conflicted there are unresolved conflicts
+ commit-pending a commit operation is waiting to be completed
+ splitting interactive rebase, commit is being split
+
-u[<mode>]::
--untracked-files[=<mode>]::
Show untracked files.
diff --git a/builtin/commit.c b/builtin/commit.c
index a17a5df..bf16cc6 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1060,6 +1060,11 @@ static int parse_and_validate_options(int argc, const char *argv[],
if (s->null_termination && status_format == STATUS_FORMAT_LONG)
status_format = STATUS_FORMAT_PORCELAIN;
+ if (s->show_sequencer && status_format != STATUS_FORMAT_SHORT) {
+ s->show_sequencer = SHOW_SEQUENCER_ONLY;
+ if (status_format == STATUS_FORMAT_LONG)
+ status_format = STATUS_FORMAT_SHORT;
+ }
if (status_format != STATUS_FORMAT_LONG)
dry_run = 1;
@@ -1156,6 +1161,8 @@ int cmd_status(int argc, const char **argv, const char *prefix)
N_("show status concisely"), STATUS_FORMAT_SHORT),
OPT_BOOLEAN('b', "branch", &s.show_branch,
N_("show branch information")),
+ OPT_SET_INT('S', "sequencer", &s.show_sequencer,
+ N_("show sequencer state information"), SHOW_SEQUENCER_YES),
OPT_SET_INT(0, "porcelain", &status_format,
N_("machine-readable output"),
STATUS_FORMAT_PORCELAIN),
@@ -1188,6 +1195,11 @@ int cmd_status(int argc, const char **argv, const char *prefix)
if (s.null_termination && status_format == STATUS_FORMAT_LONG)
status_format = STATUS_FORMAT_PORCELAIN;
+ if (s.show_sequencer && status_format != STATUS_FORMAT_SHORT) {
+ s.show_sequencer = SHOW_SEQUENCER_ONLY;
+ if (status_format == STATUS_FORMAT_LONG)
+ status_format = STATUS_FORMAT_SHORT;
+ }
handle_untracked_files_arg(&s);
if (show_ignored_in_status)
@@ -1384,6 +1396,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
OPT_SET_INT(0, "short", &status_format, N_("show status concisely"),
STATUS_FORMAT_SHORT),
OPT_BOOLEAN(0, "branch", &s.show_branch, N_("show branch information")),
+ OPT_SET_INT(0, "sequencer", &s.show_sequencer,
+ N_("show sequencer state information"), SHOW_SEQUENCER_YES),
OPT_SET_INT(0, "porcelain", &status_format,
N_("machine-readable output"), STATUS_FORMAT_PORCELAIN),
OPT_BOOLEAN('z', "null", &s.null_termination,
diff --git a/wt-status.c b/wt-status.c
index a888120..aee9745 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1201,6 +1201,34 @@ static void wt_shortstatus_print_tracking(struct wt_status *s)
fputc(s->null_termination ? '\0' : '\n', s->fp);
}
+static void wt_print_token(struct wt_status *s, const char * token_color, const char *token, int active)
+{
+ if (active) {
+ color_fprintf(s->fp, token_color, "## %s", token);
+ fputc(s->null_termination ? '\0' : '\n', s->fp);
+ }
+}
+
+static void wt_sequencer_print(struct wt_status *s)
+{
+ struct wt_status_state state;
+ const char *header_color = color(WT_STATUS_HEADER, s);
+
+ wt_status_get_state(s, &state);
+
+ wt_print_token(s, header_color, "merge", state.merge_in_progress);
+ wt_print_token(s, header_color, "am", state.am_in_progress);
+ wt_print_token(s, header_color, "rebase", state.rebase_in_progress);
+ wt_print_token(s, header_color, "rebase-interactive", state.rebase_interactive_in_progress);
+ wt_print_token(s, header_color, "cherry-pick", state.cherry_pick_in_progress);
+ wt_print_token(s, header_color, "bisect", state.bisect_in_progress);
+ wt_print_token(s, header_color, "am-empty", state.am_empty_patch);
+
+ wt_print_token(s, header_color, "conflicted", state.has_unmerged);
+ wt_print_token(s, header_color, "commit-pending", state.commit_is_pending);
+ wt_print_token(s, header_color, "splitting", state.split_in_progress);
+}
+
void wt_shortstatus_print(struct wt_status *s)
{
int i;
@@ -1208,6 +1236,13 @@ void wt_shortstatus_print(struct wt_status *s)
if (s->show_branch)
wt_shortstatus_print_tracking(s);
+ if (s->show_sequencer)
+ {
+ wt_sequencer_print(s);
+ if (s->show_sequencer == SHOW_SEQUENCER_ONLY)
+ return;
+ }
+
for (i = 0; i < s->change.nr; i++) {
struct wt_status_change_data *d;
struct string_list_item *it;
diff --git a/wt-status.h b/wt-status.h
index 0b866a2..df4b36d 100644
--- a/wt-status.h
+++ b/wt-status.h
@@ -24,6 +24,12 @@ enum untracked_status_type {
SHOW_ALL_UNTRACKED_FILES
};
+enum sequencer_status_type {
+ SHOW_SEQUENCER_NO=0,
+ SHOW_SEQUENCER_YES,
+ SHOW_SEQUENCER_ONLY
+};
+
/* from where does this commit originate */
enum commit_whence {
FROM_COMMIT, /* normal */
@@ -59,6 +65,7 @@ struct wt_status {
unsigned colopts;
int null_termination;
int show_branch;
+ enum sequencer_status_type show_sequencer;
/* These are computed during processing of the individual sections */
int commitable;
--
1.8.0.3.gde9c7d5.dirty
^ permalink raw reply related
* [PATCHv2 2/3] wt-status: More state retrieval abstraction
From: Phil Hord @ 2012-10-29 23:31 UTC (permalink / raw)
To: git
Cc: phil.hord, Jeff King, Junio C Hamano, konglu, Matthieu Moy,
Kong Lucien, Duperray Valentin, Jonas Franck, Nguy Thomas,
Phil Hord
In-Reply-To: <1351553513-20385-1-git-send-email-hordp@cisco.com>
wt_status_print_state retrieves some sequencer state
information via wt_status_get_state, but other state
information it deduces on its own. Replaces these
"local knowledge" deductions with wt_status variables
so we can share more common code in the future.
---
wt-status.c | 16 +++++++++-------
wt-status.h | 3 +++
2 files changed, 12 insertions(+), 7 deletions(-)
diff --git a/wt-status.c b/wt-status.c
index 760f52b..a888120 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -781,7 +781,7 @@ static void show_merge_in_progress(struct wt_status *s,
struct wt_status_state *state,
const char *color)
{
- if (has_unmerged(s)) {
+ if (state->has_unmerged) {
status_printf_ln(s, color, _("You have unmerged paths."));
if (advice_status_hints)
status_printf_ln(s, color,
@@ -867,9 +867,7 @@ static void show_rebase_in_progress(struct wt_status *s,
struct wt_status_state *state,
const char *color)
{
- struct stat st;
-
- if (has_unmerged(s)) {
+ if (state->has_unmerged) {
status_printf_ln(s, color, _("You are currently rebasing."));
if (advice_status_hints) {
status_printf_ln(s, color,
@@ -879,12 +877,12 @@ static void show_rebase_in_progress(struct wt_status *s,
status_printf_ln(s, color,
_(" (use \"git rebase --abort\" to check out the original branch)"));
}
- } else if (state->rebase_in_progress || !stat(git_path("MERGE_MSG"), &st)) {
+ } else if (state->rebase_in_progress || state->commit_is_pending) {
status_printf_ln(s, color, _("You are currently rebasing."));
if (advice_status_hints)
status_printf_ln(s, color,
_(" (all conflicts fixed: run \"git rebase --continue\")"));
- } else if (split_commit_in_progress(s)) {
+ } else if (state->split_in_progress) {
status_printf_ln(s, color, _("You are currently splitting a commit during a rebase."));
if (advice_status_hints)
status_printf_ln(s, color,
@@ -907,7 +905,7 @@ static void show_cherry_pick_in_progress(struct wt_status *s,
{
status_printf_ln(s, color, _("You are currently cherry-picking."));
if (advice_status_hints) {
- if (has_unmerged(s))
+ if (state->has_unmerged)
status_printf_ln(s, color,
_(" (fix conflicts and run \"git commit\")"));
else
@@ -955,6 +953,10 @@ static void wt_status_get_state(struct wt_status *s , struct wt_status_state *st
}
if (!stat(git_path("BISECT_LOG"), &st))
state->bisect_in_progress = 1;
+
+ state->has_unmerged = has_unmerged(s);
+ state->split_in_progress = split_commit_in_progress(s);
+ state->commit_is_pending = !stat(git_path("MERGE_MSG"), &st);
}
static void wt_status_print_state(struct wt_status *s)
diff --git a/wt-status.h b/wt-status.h
index 236b41f..0b866a2 100644
--- a/wt-status.h
+++ b/wt-status.h
@@ -79,6 +79,9 @@ struct wt_status_state {
int rebase_interactive_in_progress;
int cherry_pick_in_progress;
int bisect_in_progress;
+ int split_in_progress;
+ int has_unmerged;
+ int commit_is_pending;
};
void wt_status_prepare(struct wt_status *s);
--
1.8.0.3.gde9c7d5.dirty
^ permalink raw reply related
* [PATCHv2 1/3] Refactor print_state into get_state
From: Phil Hord @ 2012-10-29 23:31 UTC (permalink / raw)
To: git
Cc: phil.hord, Jeff King, Junio C Hamano, konglu, Matthieu Moy,
Kong Lucien, Duperray Valentin, Jonas Franck, Nguy Thomas,
Phil Hord
In-Reply-To: <1351553513-20385-1-git-send-email-hordp@cisco.com>
Recently git-status learned to display the state of the git
sequencer in long form to help the user remember an interrupted
command. This information is useful to other callers who do
not want it printed in the same way.
Split the new print_state function into separate get_state and
print_state functions so others can get this state info and
share common code.
Signed-off-by: Phil Hord <hordp@cisco.com>
---
wt-status.c | 31 +++++++++++++++++++------------
1 file changed, 19 insertions(+), 12 deletions(-)
diff --git a/wt-status.c b/wt-status.c
index 2a9658b..760f52b 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -928,34 +928,41 @@ static void show_bisect_in_progress(struct wt_status *s,
wt_status_print_trailer(s);
}
-static void wt_status_print_state(struct wt_status *s)
+static void wt_status_get_state(struct wt_status *s , struct wt_status_state *state)
{
- const char *state_color = color(WT_STATUS_HEADER, s);
- struct wt_status_state state;
struct stat st;
- memset(&state, 0, sizeof(state));
+ memset(state, 0, sizeof(*state));
+ /* Determine sequencer activity */
if (!stat(git_path("MERGE_HEAD"), &st)) {
- state.merge_in_progress = 1;
+ state->merge_in_progress = 1;
} else if (!stat(git_path("rebase-apply"), &st)) {
if (!stat(git_path("rebase-apply/applying"), &st)) {
- state.am_in_progress = 1;
+ state->am_in_progress = 1;
if (!stat(git_path("rebase-apply/patch"), &st) && !st.st_size)
- state.am_empty_patch = 1;
+ state->am_empty_patch = 1;
} else {
- state.rebase_in_progress = 1;
+ state->rebase_in_progress = 1;
}
} else if (!stat(git_path("rebase-merge"), &st)) {
if (!stat(git_path("rebase-merge/interactive"), &st))
- state.rebase_interactive_in_progress = 1;
+ state->rebase_interactive_in_progress = 1;
else
- state.rebase_in_progress = 1;
+ state->rebase_in_progress = 1;
} else if (!stat(git_path("CHERRY_PICK_HEAD"), &st)) {
- state.cherry_pick_in_progress = 1;
+ state->cherry_pick_in_progress = 1;
}
if (!stat(git_path("BISECT_LOG"), &st))
- state.bisect_in_progress = 1;
+ state->bisect_in_progress = 1;
+}
+
+static void wt_status_print_state(struct wt_status *s)
+{
+ const char *state_color = color(WT_STATUS_HEADER, s);
+ struct wt_status_state state;
+
+ wt_status_get_state(s, &state);
if (state.merge_in_progress)
show_merge_in_progress(s, &state, state_color);
--
1.8.0.3.gde9c7d5.dirty
^ permalink raw reply related
* [PATCHv2 0/3] git-status short sequencer state info
From: Phil Hord @ 2012-10-29 23:31 UTC (permalink / raw)
To: git
Cc: phil.hord, Jeff King, Junio C Hamano, konglu, Matthieu Moy,
Kong Lucien, Duperray Valentin, Jonas Franck, Nguy Thomas
In-Reply-To: <CABURp0qBRvZQvnBbOraQ7c7DRg8v0TjnY+MOGYaWnWwjCqi23Q@mail.gmail.com>
V2 of this patch breaks it into a series, cleans up some gaffes,
and adds more flexible display options. I still need to add some
tests. Until I do that, maybe this should be considered a WIP.
[PATCHv2 1/3] Refactor print_state into get_state
[PATCHv2 2/3] wt-status: More state retrieval abstraction
[PATCHv2 3/3] git-status: show short sequencer state
^ permalink raw reply
* Re: crash on git diff-tree -Ganything <tree> for new files with textconv filter
From: Jeff King @ 2012-10-29 22:47 UTC (permalink / raw)
To: Peter Oberndorfer; +Cc: git, Junio C Hamano
In-Reply-To: <20121029223521.GJ20513@sigill.intra.peff.net>
On Mon, Oct 29, 2012 at 06:35:21PM -0400, Jeff King wrote:
> The patch below fixes it, but it's terribly inefficient (it just detects
> the situation and reallocates). It would be much better to disable the
> reuse_worktree_file mmap when we populate the filespec, but it is too
> late to pass an option; we may have already populated from an earlier
> diffcore stage.
>
> I guess if we teach the whole diff code that "-G" (and --pickaxe-regex)
> is brittle, we can disable the optimization from the beginning based on
> the diff options. I'll take a look.
Hmm. That is problematic for two reasons.
1. The whole diff call chain will have to be modified to pass the
options around, so they can make it down to the
diff_populate_filespec level. Alternatively, we could do some kind
of global hack, which is ugly but would work OK in practice.
2. Reusing a working tree file is only half of the reason a filespec
might be mmap'd. It might also be because we are literally diffing
the working tree. "-G" was meant to be used to limit log traversal,
but it also works to reduce the diff output for something like "git
diff HEAD^".
I really wish there were an alternate regexec interface we could use
that took a pointer/size pair. Bleh.
-Peff
^ permalink raw reply
* Re: [PATCH] Document git-svn fetch --log-window-size parameter
From: Eric Wong @ 2012-10-29 22:36 UTC (permalink / raw)
To: Jeff King; +Cc: Gunnlaugur Þór Briem, git
In-Reply-To: <20121029220208.GH20513@sigill.intra.peff.net>
Jeff King <peff@peff.net> wrote:
> Since it was such a small topic, I just went ahead and queued bc22b27 in
> my repo. If that's not OK, I can revert it from 'next' and wait to get
> it by pulling from you.
That's fine, thanks!
^ permalink raw reply
* Re: crash on git diff-tree -Ganything <tree> for new files with textconv filter
From: Jeff King @ 2012-10-29 22:35 UTC (permalink / raw)
To: Peter Oberndorfer; +Cc: git, Junio C Hamano
In-Reply-To: <508EE4E4.1080407@arcor.de>
On Mon, Oct 29, 2012 at 09:19:48PM +0100, Peter Oberndorfer wrote:
> I could reproduce with my 0x3000 bytes file on linux. The buffer is not
> read with a trailing null byte it is mapped by mmap in
> diff_populate_filespec...
> So i think we will not get away with expecting a trailing null :-/
Thanks for the reproduction recipe. I was testing with "git log", which
does not use the mmap optimization.
> For me the key to reproduce the problem was to have 2 commits.
> Adding the file in the root commit it did not work. [1]
You probably would need to pass "--root" for it to do the diff of the
initial commit.
The patch below fixes it, but it's terribly inefficient (it just detects
the situation and reallocates). It would be much better to disable the
reuse_worktree_file mmap when we populate the filespec, but it is too
late to pass an option; we may have already populated from an earlier
diffcore stage.
I guess if we teach the whole diff code that "-G" (and --pickaxe-regex)
is brittle, we can disable the optimization from the beginning based on
the diff options. I'll take a look.
diff --git a/diffcore-pickaxe.c b/diffcore-pickaxe.c
index b097fa7..88d1a8f 100644
--- a/diffcore-pickaxe.c
+++ b/diffcore-pickaxe.c
@@ -80,6 +80,29 @@ static void fill_one(struct diff_filespec *one,
if (DIFF_FILE_VALID(one)) {
*textconv = get_textconv(one);
mf->size = fill_textconv(*textconv, one, &mf->ptr);
+
+ /*
+ * Horrible, horrible hack. If we are going to feed the result
+ * to regexec, we must make sure it is NUL-terminated, but we
+ * will not be if we have mmap'd a file and never munged it.
+ *
+ * We would do much better to turn off the reuse_worktree_file
+ * optimization in the first place, which is the sole source of
+ * these mmaps.
+ */
+ if (one->should_munmap && !*textconv) { mf->ptr =
+ xmallocz(one->size); memcpy(mf->ptr, one->data,
+ one->size);
+
+ /*
+ * Attach the result to the filespec, which will
+ * properly free it eventually.
+ */
+ munmap(one->data, one->size);
+ one->should_munmap = 0;
+ one->data = mf->ptr;
+ one->should_free = 1;
+ }
} else {
memset(mf, 0, sizeof(*mf));
}
^ permalink raw reply related
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