Git development
 help / color / mirror / Atom feed
* [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 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 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 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 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 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 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 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 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 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 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

* Re: [PATCH v4 00/13] New remote-hg helper
From: Felipe Contreras @ 2012-11-04  2:28 UTC (permalink / raw)
  To: Jeff King
  Cc: Michael J Gruber, Johannes Schindelin, git, Junio C Hamano,
	Sverre Rabbelier, Ilari Liusvaara, Daniel Barkalow
In-Reply-To: <CAMP44s0DyiH+ac-xnfmJ3+JSib+y8GYZZymM83HUjKi5CuqARg@mail.gmail.com>

On Fri, Nov 2, 2012 at 8:20 PM, Felipe Contreras
<felipe.contreras@gmail.com> wrote:
> On Fri, Nov 2, 2012 at 7:39 PM, Felipe Contreras
> <felipe.contreras@gmail.com> wrote:
>
>> As a rule, I don't see much value in writing a framework that works
>> only for one case, that smells more like over-engineering. If we had
>> two cases (hg and bzr), then we might be able to know with a modicum
>> of certainty what such a framework should have. So I would prefer to
>> have two standalone remote-helpers, and _then_ do a framework to
>> simplify both, but not before. But that's my personal opinion.
>>
>> Now that I have free time, I might be able to spend time writing such
>> a proof-of-concept remote-bzr, and a simple framework. But I would be
>> concentrated on remote-hg.
>
> Actually, there's no point in that; there's already a git-remote-bzr:
>
> http://bazaar.launchpad.net/~bzr-git/bzr-git/trunk/view/head:/git-remote-bzr

Turns out the quality of that tools is not that great, so I decided to
write a simple one using bzr-fastimport. It works nicely, although I
wouldn't trust the quality of bzr-fastimport too much.

It's so simple I don't see the need of a framework, but if needed, one
could be done taking these git-remote-{hg,bzr} as a basis.

Cheers.

-- 
Felipe Contreras

^ permalink raw reply

* [PATCH 0/4] Simplify and document strbuf_split() functions
From: Michael Haggerty @ 2012-11-04  6:46 UTC (permalink / raw)
  To: Jeff King; +Cc: Junio C Hamano, git, Michael Haggerty

The strbuf_split() family of functions was completely undocumented.
Add documentation and also simplify the definition of
strbuf_split_buf().

Michael Haggerty (4):
  strbuf_split_buf(): use ALLOC_GROW()
  strbuf_split_buf(): simplify iteration
  strbuf_split*(): rename "delim" parameter to "terminator"
  strbuf_split*(): document functions

 Documentation/technical/api-strbuf.txt | 16 ++++++++++++
 strbuf.c                               | 39 ++++++++++++---------------
 strbuf.h                               | 48 +++++++++++++++++++++++++++++-----
 3 files changed, 74 insertions(+), 29 deletions(-)

-- 
1.8.0

^ permalink raw reply

* [PATCH 1/4] strbuf_split_buf(): use ALLOC_GROW()
From: Michael Haggerty @ 2012-11-04  6:46 UTC (permalink / raw)
  To: Jeff King; +Cc: Junio C Hamano, git, Michael Haggerty
In-Reply-To: <1352011614-29334-1-git-send-email-mhagger@alum.mit.edu>

Use ALLOC_GROW() rather than inline code to manage memory in
strbuf_split_buf().  Rename "pos" to "nr" because it better describes
the use of the variable and it better conforms to the "ALLOC_GROW"
idiom.

Also, instead of adding a sentinal NULL value after each entry is
added to the list, only add it once after all of the entries have been
added.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 strbuf.c | 17 +++++++----------
 1 file changed, 7 insertions(+), 10 deletions(-)

diff --git a/strbuf.c b/strbuf.c
index 4b9e30c..5256c2a 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -108,33 +108,30 @@ void strbuf_ltrim(struct strbuf *sb)
 
 struct strbuf **strbuf_split_buf(const char *str, size_t slen, int delim, int max)
 {
-	int alloc = 2, pos = 0;
+	struct strbuf **ret = NULL;
+	size_t nr = 0, alloc = 0;
 	const char *n, *p;
-	struct strbuf **ret;
 	struct strbuf *t;
 
-	ret = xcalloc(alloc, sizeof(struct strbuf *));
 	p = n = str;
 	while (n < str + slen) {
 		int len;
-		if (max <= 0 || pos + 1 < max)
+		if (max <= 0 || nr + 1 < max)
 			n = memchr(n, delim, slen - (n - str));
 		else
 			n = NULL;
-		if (pos + 1 >= alloc) {
-			alloc = alloc * 2;
-			ret = xrealloc(ret, sizeof(struct strbuf *) * alloc);
-		}
 		if (!n)
 			n = str + slen - 1;
 		len = n - p + 1;
 		t = xmalloc(sizeof(struct strbuf));
 		strbuf_init(t, len);
 		strbuf_add(t, p, len);
-		ret[pos] = t;
-		ret[++pos] = NULL;
+		ALLOC_GROW(ret, nr + 2, alloc);
+		ret[nr++] = t;
 		p = ++n;
 	}
+	ALLOC_GROW(ret, nr + 1, alloc); /* In case string was empty */
+	ret[nr] = NULL;
 	return ret;
 }
 
-- 
1.8.0

^ permalink raw reply related

* [PATCH 2/4] strbuf_split_buf(): simplify iteration
From: Michael Haggerty @ 2012-11-04  6:46 UTC (permalink / raw)
  To: Jeff King; +Cc: Junio C Hamano, git, Michael Haggerty
In-Reply-To: <1352011614-29334-1-git-send-email-mhagger@alum.mit.edu>

While iterating, update str and slen to keep track of the part of the
string that hasn't been processed yet rather than computing things
relative to the start of the original string.  This eliminates one
local variable, reduces the scope of another, and reduces the amount
of arithmetic needed within the loop.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 strbuf.c | 23 ++++++++++-------------
 1 file changed, 10 insertions(+), 13 deletions(-)

diff --git a/strbuf.c b/strbuf.c
index 5256c2a..c7cd529 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -110,25 +110,22 @@ struct strbuf **strbuf_split_buf(const char *str, size_t slen, int delim, int ma
 {
 	struct strbuf **ret = NULL;
 	size_t nr = 0, alloc = 0;
-	const char *n, *p;
 	struct strbuf *t;
 
-	p = n = str;
-	while (n < str + slen) {
-		int len;
-		if (max <= 0 || nr + 1 < max)
-			n = memchr(n, delim, slen - (n - str));
-		else
-			n = NULL;
-		if (!n)
-			n = str + slen - 1;
-		len = n - p + 1;
+	while (slen) {
+		int len = slen;
+		if (max <= 0 || nr + 1 < max) {
+			const char *end = memchr(str, delim, slen);
+			if (end)
+				len = end - str + 1;
+		}
 		t = xmalloc(sizeof(struct strbuf));
 		strbuf_init(t, len);
-		strbuf_add(t, p, len);
+		strbuf_add(t, str, len);
 		ALLOC_GROW(ret, nr + 2, alloc);
 		ret[nr++] = t;
-		p = ++n;
+		str += len;
+		slen -= len;
 	}
 	ALLOC_GROW(ret, nr + 1, alloc); /* In case string was empty */
 	ret[nr] = NULL;
-- 
1.8.0

^ permalink raw reply related

* [PATCH 3/4] strbuf_split*(): rename "delim" parameter to "terminator"
From: Michael Haggerty @ 2012-11-04  6:46 UTC (permalink / raw)
  To: Jeff King; +Cc: Junio C Hamano, git, Michael Haggerty
In-Reply-To: <1352011614-29334-1-git-send-email-mhagger@alum.mit.edu>

The word "delimiter" suggests that the argument separates the
substrings, whereas in fact (1) the delimiter characters are included
in the output, and (2) if the input string ends with the delimiter,
then the output does not include a final empty string.  So rename the
"delim" arguments of the strbuf_split() family of functions to
"terminator", which is more suggestive of how it is used.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 strbuf.c |  5 +++--
 strbuf.h | 15 ++++++++-------
 2 files changed, 11 insertions(+), 9 deletions(-)

diff --git a/strbuf.c b/strbuf.c
index c7cd529..05d0693 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -106,7 +106,8 @@ void strbuf_ltrim(struct strbuf *sb)
 	sb->buf[sb->len] = '\0';
 }
 
-struct strbuf **strbuf_split_buf(const char *str, size_t slen, int delim, int max)
+struct strbuf **strbuf_split_buf(const char *str, size_t slen,
+				 int terminator, int max)
 {
 	struct strbuf **ret = NULL;
 	size_t nr = 0, alloc = 0;
@@ -115,7 +116,7 @@ struct strbuf **strbuf_split_buf(const char *str, size_t slen, int delim, int ma
 	while (slen) {
 		int len = slen;
 		if (max <= 0 || nr + 1 < max) {
-			const char *end = memchr(str, delim, slen);
+			const char *end = memchr(str, terminator, slen);
 			if (end)
 				len = end - str + 1;
 		}
diff --git a/strbuf.h b/strbuf.h
index be941ee..c896a47 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -45,20 +45,21 @@ extern void strbuf_ltrim(struct strbuf *);
 extern int strbuf_cmp(const struct strbuf *, const struct strbuf *);
 
 extern struct strbuf **strbuf_split_buf(const char *, size_t,
-					int delim, int max);
+					int terminator, int max);
 static inline struct strbuf **strbuf_split_str(const char *str,
-					       int delim, int max)
+					       int terminator, int max)
 {
-	return strbuf_split_buf(str, strlen(str), delim, max);
+	return strbuf_split_buf(str, strlen(str), terminator, max);
 }
 static inline struct strbuf **strbuf_split_max(const struct strbuf *sb,
-						int delim, int max)
+						int terminator, int max)
 {
-	return strbuf_split_buf(sb->buf, sb->len, delim, max);
+	return strbuf_split_buf(sb->buf, sb->len, terminator, max);
 }
-static inline struct strbuf **strbuf_split(const struct strbuf *sb, int delim)
+static inline struct strbuf **strbuf_split(const struct strbuf *sb,
+					   int terminator)
 {
-	return strbuf_split_max(sb, delim, 0);
+	return strbuf_split_max(sb, terminator, 0);
 }
 extern void strbuf_list_free(struct strbuf **);
 
-- 
1.8.0

^ permalink raw reply related

* [PATCH 4/4] strbuf_split*(): document functions
From: Michael Haggerty @ 2012-11-04  6:46 UTC (permalink / raw)
  To: Jeff King; +Cc: Junio C Hamano, git, Michael Haggerty
In-Reply-To: <1352011614-29334-1-git-send-email-mhagger@alum.mit.edu>

Document strbuf_split_buf(), strbuf_split_str(), strbuf_split_max(),
strbuf_split(), and strbuf_list_free() in the header file and in
api-strbuf.txt.  (These functions were previously completely
undocumented.)

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 Documentation/technical/api-strbuf.txt | 16 ++++++++++++++++
 strbuf.h                               | 33 +++++++++++++++++++++++++++++++++
 2 files changed, 49 insertions(+)

diff --git a/Documentation/technical/api-strbuf.txt b/Documentation/technical/api-strbuf.txt
index 95a8bf3..84686b5 100644
--- a/Documentation/technical/api-strbuf.txt
+++ b/Documentation/technical/api-strbuf.txt
@@ -279,6 +279,22 @@ same behaviour as well.
 	Strip whitespace from a buffer. The second parameter controls if
 	comments are considered contents to be removed or not.
 
+`strbuf_split_buf`::
+`strbuf_split_str`::
+`strbuf_split_max`::
+`strbuf_split`::
+
+	Split a string or strbuf into a list of strbufs at a specified
+	terminator character.  The returned substrings include the
+	terminator characters.  Some of these functions take a `max`
+	parameter, which, if positive, limits the output to that
+	number of substrings.
+
+`strbuf_list_free`::
+
+	Free a list of strbufs (for example, the return values of the
+	`strbuf_split()` functions).
+
 `launch_editor`::
 
 	Launch the user preferred editor to edit a file and fill the buffer
diff --git a/strbuf.h b/strbuf.h
index c896a47..aa386c6 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -44,23 +44,56 @@ extern void strbuf_rtrim(struct strbuf *);
 extern void strbuf_ltrim(struct strbuf *);
 extern int strbuf_cmp(const struct strbuf *, const struct strbuf *);
 
+/*
+ * Split str (of length slen) at the specified terminator character.
+ * Return a null-terminated array of pointers to strbuf objects
+ * holding the substrings.  The substrings include the terminator,
+ * except for the last substring, which might be unterminated if the
+ * original string did not end with a terminator.  If max is positive,
+ * then split the string into at most max substrings (with the last
+ * substring containing everything following the (max-1)th terminator
+ * character).
+ *
+ * For lighter-weight alternatives, see string_list_split() and
+ * string_list_split_in_place().
+ */
 extern struct strbuf **strbuf_split_buf(const char *, size_t,
 					int terminator, int max);
+
+/*
+ * Split a NUL-terminated string at the specified terminator
+ * character.  See strbuf_split_buf() for more information.
+ */
 static inline struct strbuf **strbuf_split_str(const char *str,
 					       int terminator, int max)
 {
 	return strbuf_split_buf(str, strlen(str), terminator, max);
 }
+
+/*
+ * Split a strbuf at the specified terminator character.  See
+ * strbuf_split_buf() for more information.
+ */
 static inline struct strbuf **strbuf_split_max(const struct strbuf *sb,
 						int terminator, int max)
 {
 	return strbuf_split_buf(sb->buf, sb->len, terminator, max);
 }
+
+/*
+ * Split a strbuf at the specified terminator character.  See
+ * strbuf_split_buf() for more information.
+ */
 static inline struct strbuf **strbuf_split(const struct strbuf *sb,
 					   int terminator)
 {
 	return strbuf_split_max(sb, terminator, 0);
 }
+
+/*
+ * Free a NULL-terminated list of strbufs (for example, the return
+ * values of the strbuf_split*() functions).
+ */
 extern void strbuf_list_free(struct strbuf **);
 
 /*----- add data in your buffer -----*/
-- 
1.8.0

^ permalink raw reply related

* [PATCH 0/5] Use string_lists when processing notes
From: Michael Haggerty @ 2012-11-04  7:07 UTC (permalink / raw)
  To: Jeff King; +Cc: Junio C Hamano, git, Michael Haggerty

This simplifies the code.  Also, sort lines all at once (O(N lg N))
rather than insertion sorting as lines are processed (O(N^2)) and fix
the handling of empty values in GIT_NOTES_DISPLAY_REF and
GIT_NOTES_REWRITE_REF.

Michael Haggerty (5):
  string_list: add a function string_list_remove_empty_items()
  Initialize sort_uniq_list using named constant
  combine_notes_cat_sort_uniq(): sort and dedup lines all at once
  notes: fix handling of colon-separated values
  string_list_add_refs_from_colon_sep(): use string_list_split()

 Documentation/technical/api-string-list.txt |  9 ++++-
 notes.c                                     | 61 ++++++++++++-----------------
 string-list.c                               |  9 +++++
 string-list.h                               |  7 ++++
 4 files changed, 49 insertions(+), 37 deletions(-)

-- 
1.8.0

^ permalink raw reply

* [PATCH 1/5] string_list: add a function string_list_remove_empty_items()
From: Michael Haggerty @ 2012-11-04  7:07 UTC (permalink / raw)
  To: Jeff King; +Cc: Junio C Hamano, git, Michael Haggerty
In-Reply-To: <1352012830-13591-1-git-send-email-mhagger@alum.mit.edu>


Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 Documentation/technical/api-string-list.txt | 9 ++++++++-
 string-list.c                               | 9 +++++++++
 string-list.h                               | 7 +++++++
 3 files changed, 24 insertions(+), 1 deletion(-)

diff --git a/Documentation/technical/api-string-list.txt b/Documentation/technical/api-string-list.txt
index 94d7a2b..7386bca 100644
--- a/Documentation/technical/api-string-list.txt
+++ b/Documentation/technical/api-string-list.txt
@@ -38,7 +38,8 @@ member (you need this if you add things later) and you should set the
   `unsorted_string_list_delete_item`.
 
 . Can remove items not matching a criterion from a sorted or unsorted
-  list using `filter_string_list`.
+  list using `filter_string_list`, or remove empty strings using
+  `string_list_remove_empty_items`.
 
 . Finally it should free the list using `string_list_clear`.
 
@@ -75,6 +76,12 @@ Functions
 	to be deleted.  Preserve the order of the items that are
 	retained.
 
+`string_list_remove_empty_items`::
+
+	Remove any empty strings from the list.  If free_util is true,
+	call free() on the util members of any items that have to be
+	deleted.  Preserve the order of the items that are retained.
+
 `string_list_longest_prefix`::
 
 	Return the longest string within a string_list that is a
diff --git a/string-list.c b/string-list.c
index c54b816..397e6cf 100644
--- a/string-list.c
+++ b/string-list.c
@@ -136,6 +136,15 @@ void filter_string_list(struct string_list *list, int free_util,
 	list->nr = dst;
 }
 
+static int item_is_not_empty(struct string_list_item *item, void *unused)
+{
+	return *item->string != '\0';
+}
+
+void string_list_remove_empty_items(struct string_list *list, int free_util) {
+	filter_string_list(list, free_util, item_is_not_empty, NULL);
+}
+
 char *string_list_longest_prefix(const struct string_list *prefixes,
 				 const char *string)
 {
diff --git a/string-list.h b/string-list.h
index 5efd07b..c50b0d0 100644
--- a/string-list.h
+++ b/string-list.h
@@ -39,6 +39,13 @@ void filter_string_list(struct string_list *list, int free_util,
 			string_list_each_func_t want, void *cb_data);
 
 /*
+ * Remove any empty strings from the list.  If free_util is true, call
+ * free() on the util members of any items that have to be deleted.
+ * Preserve the order of the items that are retained.
+ */
+void string_list_remove_empty_items(struct string_list *list, int free_util);
+
+/*
  * Return the longest string in prefixes that is a prefix (in the
  * sense of prefixcmp()) of string, or NULL if no such prefix exists.
  * This function does not require the string_list to be sorted (it
-- 
1.8.0

^ permalink raw reply related

* [PATCH 2/5] Initialize sort_uniq_list using named constant
From: Michael Haggerty @ 2012-11-04  7:07 UTC (permalink / raw)
  To: Jeff King; +Cc: Junio C Hamano, git, Michael Haggerty
In-Reply-To: <1352012830-13591-1-git-send-email-mhagger@alum.mit.edu>


Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 notes.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/notes.c b/notes.c
index bc454e1..8652f8f 100644
--- a/notes.c
+++ b/notes.c
@@ -901,7 +901,7 @@ static int string_list_join_lines_helper(struct string_list_item *item,
 int combine_notes_cat_sort_uniq(unsigned char *cur_sha1,
 		const unsigned char *new_sha1)
 {
-	struct string_list sort_uniq_list = { NULL, 0, 0, 1 };
+	struct string_list sort_uniq_list = STRING_LIST_INIT_DUP;
 	struct strbuf buf = STRBUF_INIT;
 	int ret = 1;
 
-- 
1.8.0

^ permalink raw reply related

* [PATCH 3/5] combine_notes_cat_sort_uniq(): sort and dedup lines all at once
From: Michael Haggerty @ 2012-11-04  7:07 UTC (permalink / raw)
  To: Jeff King; +Cc: Junio C Hamano, git, Michael Haggerty
In-Reply-To: <1352012830-13591-1-git-send-email-mhagger@alum.mit.edu>

Instead of reading lines one by one and insertion-sorting them into a
string_list, read all of the lines, sort them, then remove duplicates.
Aside from being less code, this reduces the complexity from O(N^2) to
O(N lg N) in the total number of lines.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 notes.c | 38 ++++++++++++++++----------------------
 1 file changed, 16 insertions(+), 22 deletions(-)

diff --git a/notes.c b/notes.c
index 8652f8f..62f8f6f 100644
--- a/notes.c
+++ b/notes.c
@@ -848,15 +848,16 @@ int combine_notes_ignore(unsigned char *cur_sha1,
 	return 0;
 }
 
-static int string_list_add_note_lines(struct string_list *sort_uniq_list,
+/*
+ * Add the lines from the named object to list, with trailing
+ * newlines removed.
+ */
+static int string_list_add_note_lines(struct string_list *list,
 				      const unsigned char *sha1)
 {
 	char *data;
 	unsigned long len;
 	enum object_type t;
-	struct strbuf buf = STRBUF_INIT;
-	struct strbuf **lines = NULL;
-	int i, list_index;
 
 	if (is_null_sha1(sha1))
 		return 0;
@@ -868,24 +869,14 @@ static int string_list_add_note_lines(struct string_list *sort_uniq_list,
 		return t != OBJ_BLOB || !data;
 	}
 
-	strbuf_attach(&buf, data, len, len + 1);
-	lines = strbuf_split(&buf, '\n');
-
-	for (i = 0; lines[i]; i++) {
-		if (lines[i]->buf[lines[i]->len - 1] == '\n')
-			strbuf_setlen(lines[i], lines[i]->len - 1);
-		if (!lines[i]->len)
-			continue; /* skip empty lines */
-		list_index = string_list_find_insert_index(sort_uniq_list,
-							   lines[i]->buf, 0);
-		if (list_index < 0)
-			continue; /* skip duplicate lines */
-		string_list_insert_at_index(sort_uniq_list, list_index,
-					    lines[i]->buf);
-	}
-
-	strbuf_list_free(lines);
-	strbuf_release(&buf);
+	/*
+	 * If the last line of the file is EOL-terminated, this will
+	 * add an empty string to the list.  But it will be removed
+	 * later, along with any empty strings that came from empty
+	 * lines within the file.
+	 */
+	string_list_split(list, data, '\n', -1);
+	free(data);
 	return 0;
 }
 
@@ -910,6 +901,9 @@ int combine_notes_cat_sort_uniq(unsigned char *cur_sha1,
 		goto out;
 	if (string_list_add_note_lines(&sort_uniq_list, new_sha1))
 		goto out;
+	string_list_remove_empty_items(&sort_uniq_list, 0);
+	sort_string_list(&sort_uniq_list);
+	string_list_remove_duplicates(&sort_uniq_list, 0);
 
 	/* create a new blob object from sort_uniq_list */
 	if (for_each_string_list(&sort_uniq_list,
-- 
1.8.0

^ permalink raw reply related

* [PATCH 4/5] notes: fix handling of colon-separated values
From: Michael Haggerty @ 2012-11-04  7:07 UTC (permalink / raw)
  To: Jeff King; +Cc: Junio C Hamano, git, Michael Haggerty
In-Reply-To: <1352012830-13591-1-git-send-email-mhagger@alum.mit.edu>

The substrings output by strbuf_split() include the ':' delimiters.
When processing GIT_NOTES_DISPLAY_REF and GIT_NOTES_REWRITE_REF, strip
off the delimiter character *before* checking whether the substring is
empty rather than after, so that empty strings within the list are
also skipped.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 notes.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/notes.c b/notes.c
index 62f8f6f..63b2a09 100644
--- a/notes.c
+++ b/notes.c
@@ -951,10 +951,10 @@ void string_list_add_refs_from_colon_sep(struct string_list *list,
 	split = strbuf_split(&globbuf, ':');
 
 	for (i = 0; split[i]; i++) {
+		if (split[i]->len && split[i]->buf[split[i]->len-1] == ':')
+			strbuf_setlen(split[i], split[i]->len-1);
 		if (!split[i]->len)
 			continue;
-		if (split[i]->buf[split[i]->len-1] == ':')
-			strbuf_setlen(split[i], split[i]->len-1);
 		string_list_add_refs_by_glob(list, split[i]->buf);
 	}
 
-- 
1.8.0

^ permalink raw reply related

* [PATCH 5/5] string_list_add_refs_from_colon_sep(): use string_list_split()
From: Michael Haggerty @ 2012-11-04  7:07 UTC (permalink / raw)
  To: Jeff King; +Cc: Junio C Hamano, git, Michael Haggerty
In-Reply-To: <1352012830-13591-1-git-send-email-mhagger@alum.mit.edu>

It makes for simpler code than strbuf_split().

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 notes.c | 21 ++++++++-------------
 1 file changed, 8 insertions(+), 13 deletions(-)

diff --git a/notes.c b/notes.c
index 63b2a09..b823701 100644
--- a/notes.c
+++ b/notes.c
@@ -943,23 +943,18 @@ void string_list_add_refs_by_glob(struct string_list *list, const char *glob)
 void string_list_add_refs_from_colon_sep(struct string_list *list,
 					 const char *globs)
 {
-	struct strbuf globbuf = STRBUF_INIT;
-	struct strbuf **split;
+	struct string_list split = STRING_LIST_INIT_NODUP;
+	char *globs_copy = xstrdup(globs);
 	int i;
 
-	strbuf_addstr(&globbuf, globs);
-	split = strbuf_split(&globbuf, ':');
+	string_list_split_in_place(&split, globs_copy, ':', -1);
+	string_list_remove_empty_items(&split, 0);
 
-	for (i = 0; split[i]; i++) {
-		if (split[i]->len && split[i]->buf[split[i]->len-1] == ':')
-			strbuf_setlen(split[i], split[i]->len-1);
-		if (!split[i]->len)
-			continue;
-		string_list_add_refs_by_glob(list, split[i]->buf);
-	}
+	for (i = 0; i < split.nr; i++)
+		string_list_add_refs_by_glob(list, split.items[i].string);
 
-	strbuf_list_free(split);
-	strbuf_release(&globbuf);
+	string_list_clear(&split, 0);
+	free(globs_copy);
 }
 
 static int notes_display_config(const char *k, const char *v, void *cb)
-- 
1.8.0

^ permalink raw reply related

* Re: Wrap commit messages on `git commit -m`
From: Tay Ray Chuan @ 2012-11-04  7:27 UTC (permalink / raw)
  To: Lars Gullik Bjønnes; +Cc: Ramkumar Ramachandra, git
In-Reply-To: <m3a9v170ca.fsf@black.gullik.net>

On Thu, Nov 1, 2012 at 11:29 AM, Lars Gullik Bjønnes <larsbj@gullik.org> wrote:
> Ramkumar Ramachandra <artagnon@gmail.com> writes:
>
> | Hi,
>>
> | Some of my colleagues are lazy to fire up an editor and write proper
> | commit messages- they often write one-liners using `git commit -m`.
> | However, that line turns out to be longer than 72 characters, and the
> | resulting `git log` output is ugly.  So, I was wondering if it would
> | be a good idea to wrap these one-liners to 72 characters
> | automatically.
>
> git commit -m 'foo: fix this problem
>
> This problem is fixed by doing foo,
> bar and baz.
>
> Signed-off-by: me
> '
>
> works.

Perhaps a deeper issue is that the implicit email format
(subject-body) for commit messages, is, well, implicit. New users of
git who type git-commit -m '...' isn't going to know that those few
characters will all be lumped on a "subject" line, forever screwing
themselves when they review the output of git-log, git-rebase
--interactive, etc. (can't remember off the top of my head if
git-format-patch would chop off long subjects and move it to the
body), which may be a significant period of time (and thus commits)
later.

While I don't have any ideas on how to improve on this, hopefully this
gets recognized as an issue in the first place.

-- 
Cheers,
Ray Chuan

^ permalink raw reply

* Re: [PATCH 1/4] strbuf_split_buf(): use ALLOC_GROW()
From: Jeff King @ 2012-11-04 11:41 UTC (permalink / raw)
  To: Michael Haggerty; +Cc: Junio C Hamano, git
In-Reply-To: <1352011614-29334-2-git-send-email-mhagger@alum.mit.edu>

On Sun, Nov 04, 2012 at 07:46:51AM +0100, Michael Haggerty wrote:

> Use ALLOC_GROW() rather than inline code to manage memory in
> strbuf_split_buf().  Rename "pos" to "nr" because it better describes
> the use of the variable and it better conforms to the "ALLOC_GROW"
> idiom.

I suspect this was not used originally because ALLOC_GROW relies on
alloc_nr, which does fast growth early on. At (x+16)*3/2, we end up with
24 slots for the first allocation. We are typically splitting 1 or 2
values.

It probably doesn't make a big difference in practice, though, as we're
talking about wasting less than 200 bytes on a 64-bit platform, and we
do not tend to keep large numbers of split lists around.

-Peff

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox