[PATCH 00/15] Synchronize with git-remote-hg

14 views
Skip to first unread message

Felipe Contreras

unread,
May 3, 2014, 9:55:16 PM5/3/14
to giti...@googlegroups.com, Felipe Contreras
Hi,

The modern gitifyhg was, let's be honest, a glorified copy of git-remote-hg. At
some point in time it might have had some unique features or fixes, but that
hasn't been the case since a long long time.

Nowadays git-remote-hg has all the features of gitifyhg, but the same cannot be
said of the latter.

I already went in detail comparing the results and differences of both running
in both test suites, so I'm not going to go through that again because most of
the discrepancies are still trivial, or are already resolved.

The only difference worth mentioning is in the way both handle divergence, aka
non-fast-forwards, aka multiple heads.

The approach of gitifyhg, as I understand it, is that it doesn't force a push,
instead, it there's an error it catches it and localy strips the revisions that
were being pushed. This ensures that locally the multiple heads are not stored,
and once the user has either merged or rebased, the previous conflicting heads
wouldn't make the push fail again. While this approach has some merit, it
requires an extension.

The approach of git-remote-hg is to specify precisely what heads to push and
using a custom push() method, which can report if there will be an issue before
issuing the actual push. This means that even if there are multiple heads, the
extra heads don't have to be removed locally. It also means there's finer
control over notifications and options.

For example, the custom push function allows the use of --force, and --dry-run,
and each individual branch (or bookmark) gets a nice notification if a push was
forced or if a fetch is needed, even when running with --dry-run. If a
non-force push fails, the branches that caused it are reported.

In addition, this allows pushing new bookmarks even if the branch would have
multiple heads.

In gitifyhg none of that is possible; --force and --dry-run fail, and when a
non-force push fails, all the branches get the notification of
non-fast-forward.

git-remote-hg has received constant feedback and many people are using it now.
In fact, it's going to be distributed by default in Git v2.0 since it has been
proved to be solid and stable.

So it is time to accept this fact, salvage the most possible from gitifyhg,
abandon the project and move to git-remote-hg.

In order to make sure as few things as possible are lost I have made changes to
both projects to make it easier to compare them, and in the process I found a
bunch of issues in gitifyhg and fixed them.

This patch series is the result of this work.

With these patches applied I run the git-remote-hg test-suite, and these are the results.

5811-remote-hg-bidi.sh (Wstat: 256 Tests: 5 Failed: 3)
Failed tests: 3-5
Non-zero exit status: 1
t5812-remote-hg-hg-git.sh (Wstat: 256 Tests: 11 Failed: 11)
Failed tests: 1-11
Non-zero exit status: 1
t5810-remote-hg.sh (Wstat: 256 Tests: 34 Failed: 8)
Failed tests: 16-17, 20-23, 25-26

== remote update bookmark diverge ==

Doesn't support fetch-first reports.

== remote new bookmark multiple branch head ==

Cannot push a new bookmark if there are multiple heads in the branch.

== remote big push ==

Reports all branches as non-fast-forward:

! [rejected] master -> master (non-fast-forward)
! [rejected] branches/good_branch -> branches/good_branch (non-fast-forward)
! [rejected] branches/bad_branch -> branches/bad_branch (non-fast-forward)
! [rejected] bad_bmark1 -> bad_bmark1 (non-fast-forward)
! [rejected] bad_bmark2 -> bad_bmark2 (non-fast-forward)
! [rejected] good_bmark -> good_bmark (non-fast-forward)
! [rejected] branches/new_branch -> branches/new_branch (non-fast-forward)
! [rejected] new_bmark -> new_bmark (non-fast-forward)

While git-remote-hg reports the problems individiually:

1f23d0e..6f9aac3 branches/good_branch -> branches/good_branch
0aa76de..6658520 master -> master
0aa76de..c8f4933 good_bmark -> good_bmark
* [new branch] branches/new_branch -> branches/new_branch
* [new branch] new_bmark -> new_bmark
! [rejected] branches/bad_branch -> branches/bad_branch (non-fast-forward)
! [rejected] bad_bmark1 -> bad_bmark1 (non-fast-forward)
! [rejected] bad_bmark2 -> bad_bmark2 (non-fast-forward)

== remote big push fetch first ==

Similar issue but reporting fetch-first (all of them non-fast-forward).

== remote big push force ==

--force is not supported; the push fail reporting all of them non-fast-forward.

== remote big push dry-run ==

--dry-run is not supported.

== remote double failed push ==

Crashes.

== clone remote with null bookmark, then push ==

Crashes.

== git tags ==
== hg tags ==
== hg branch ==

All of these bidirectional tests have problems.

Solving these problems would require a major amount of work, and why go through
that if we already have git-remote-hg?

On the other hand running gitifyhg tests with git-remote-hg:

test_anonymous_branches.t ..... ok
test_clone_file_operations.t .. ok
test_author.t ................. ok
test_bookmarks.t .............. ok
test_clone.t .................. ok
test_notes.t .................. ok
test_push_tags.t .............. ok
test_pull.t ................... ok
test_special_cases.t .......... ok
test_spaces.t ................. ok
test_push.t ................... ok
All tests successful.

In fact there is a test that git-remote-hg passes and gitifyhg doesn't:

ok 8 - pull_from_bookmark # TODO known breakage vanished

So it is clear that git-remote-hg is the better solution, but is there anything
to salvage from gitifyhg?

The only thing I can think of is the tests, so I went one by one to see what
isn't been tested by git-remote-hg test suite. I made a list, and I can post it
here if you want, but in the only a few tests would be useful, so I ported
them.

https://github.com/felipec/git/commit/493612391cc590429fa1a76e6b2f96353156ee0b
https://github.com/felipec/git/commit/2e2c73938c70d02c3905a9f0e35222b9d7beae20

The only remaining tests that have not been ported are the ones that check
spaces in refs.

So I'm farily confident the git-remote-hg test suite covers what the gitifyhg
test suite covered. It might make sense to port a few things more so the tests

What do you think?


Felipe Contreras (15):
test: simplify python path stuff
test: simplify testgitifyhg hack
test: fix user.name configuration
Fix Git body
Use Git information for tags
Fix order of file operations
Fix for temporary remotes
Allow non-fast-forward updates
test: fix broken && chain
test: simplify helpers
test: simplify hg user config
test: fix file quotes
Track the 'default' branch
Allow tags to be overridden
Port git-remote-hg author sanitizer

MANIFEST.in | 2 +-
git-remote-testgitifyhg | 19 -----------
gitifyhg/gitexporter.py | 14 ++++++--
gitifyhg/gitifyhg.py | 19 ++++++-----
gitifyhg/hgimporter.py | 59 +++++++++++++++++++--------------
gitifyhg/util.py | 5 +--
test/git-remote-gitifyhg | 7 ++++
test/test-lib.sh | 68 ++++++++++++---------------------------
test/test_anonymous_branches.t | 4 +--
test/test_author.t | 6 ++--
test/test_bookmarks.t | 3 ++
test/test_clone.t | 36 +++++++++++++++++++++
test/test_clone_file_operations.t | 4 +--
test/test_notes.t | 2 +-
test/test_pull.t | 2 +-
test/test_push.t | 29 +++++++++++++++--
test/test_push_tags.t | 15 +++------
test/test_spaces.t | 2 ++
test/test_special_cases.t | 10 +++---
19 files changed, 171 insertions(+), 135 deletions(-)
delete mode 100755 git-remote-testgitifyhg
create mode 100755 test/git-remote-gitifyhg

--
1.9.2+fc1.20.g204a630

Felipe Contreras

unread,
May 3, 2014, 9:55:17 PM5/3/14
to giti...@googlegroups.com, Felipe Contreras
Signed-off-by: Felipe Contreras <felipe.c...@gmail.com>
---
git-remote-testgitifyhg | 16 ++--------------
test/test-lib.sh | 1 +
2 files changed, 3 insertions(+), 14 deletions(-)

diff --git a/git-remote-testgitifyhg b/git-remote-testgitifyhg
index 6a677a4..c261329 100755
--- a/git-remote-testgitifyhg
+++ b/git-remote-testgitifyhg
@@ -2,18 +2,6 @@
#
# This bin script is intended only for testing.
# It will not be used by the installed egg.
-#
-# For testing, just put the path to this bin script ahead of all
-# others, e.g.:
-# export PATH=/path/to/gitifyhg/bin:$PATH
-
-import os
-import sys
-
-# Set up sys.path so our package is before all others
-# (including eggs of ourselves that have been installed).
-bindir = os.path.dirname(__file__)
-sys.path.insert(1, os.path.abspath(os.path.join(bindir, "..")))

-from gitifyhg import gitifyhg
-gitifyhg.main()
+import gitifyhg.gitifyhg
+gitifyhg.gitifyhg.main()
diff --git a/test/test-lib.sh b/test/test-lib.sh
index cd5c4b0..b009392 100644
--- a/test/test-lib.sh
+++ b/test/test-lib.sh
@@ -11,6 +11,7 @@ export GIT_PAGER=cat
export HGRCPATH="$HOME/.hgrc"
export NL='
'
+export PYTHONPATH="$SHARNESS_BUILD_DIRECTORY"

make_hg_repo() {
hg init hg_repo &&
--
1.9.2+fc1.20.g204a630

Felipe Contreras

unread,
May 3, 2014, 9:55:18 PM5/3/14
to giti...@googlegroups.com, Felipe Contreras
Move it to the 'test' directory so it's clear what's its purpose, and
rename it so the tests use less hacky URLs.

Signed-off-by: Felipe Contreras <felipe.c...@gmail.com>
---
MANIFEST.in | 2 +-
git-remote-testgitifyhg => test/git-remote-gitifyhg | 0
test/test-lib.sh | 3 ++-
test/test_anonymous_branches.t | 4 ++--
test/test_notes.t | 2 +-
test/test_pull.t | 2 +-
test/test_special_cases.t | 2 +-
7 files changed, 8 insertions(+), 7 deletions(-)
rename git-remote-testgitifyhg => test/git-remote-gitifyhg (100%)

diff --git a/MANIFEST.in b/MANIFEST.in
index e4859b2..079ded8 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,3 +1,3 @@
include LICENSE.txt
-include git-remote-testgitifyhg
+include test/git-remote-gitifyhg
recursive-include test Makefile *.sh *.t
diff --git a/git-remote-testgitifyhg b/test/git-remote-gitifyhg
similarity index 100%
rename from git-remote-testgitifyhg
rename to test/git-remote-gitifyhg
diff --git a/test/test-lib.sh b/test/test-lib.sh
index b009392..69015f9 100644
--- a/test/test-lib.sh
+++ b/test/test-lib.sh
@@ -12,6 +12,7 @@ export HGRCPATH="$HOME/.hgrc"
export NL='
'
export PYTHONPATH="$SHARNESS_BUILD_DIRECTORY"
+export PATH="$SHARNESS_TEST_DIRECTORY:$PATH"

make_hg_repo() {
hg init hg_repo &&
@@ -23,7 +24,7 @@ make_hg_repo() {

clone_repo() {
cd .. &&
- test_expect_code 0 git clone "testgitifyhg::hg_repo" git_clone &&
+ test_expect_code 0 git clone "gitifyhg::hg_repo" git_clone &&
cd git_clone &&
git config user.email $GIT_AUTHOR_EMAIL &&
git config user.name "$GIT_USER"
diff --git a/test/test_anonymous_branches.t b/test/test_anonymous_branches.t
index 09f23d1..dbdf4e2 100755
--- a/test/test_anonymous_branches.t
+++ b/test/test_anonymous_branches.t
@@ -23,7 +23,7 @@ test_expect_failure 'anonymous branches dont work' '
make_hg_commit c test_file &&
cd .. &&

- git clone testgitifyhg::hg_repo git_clone 2>&1 | grep "more than one head" &&
+ git clone gitifyhg::hg_repo git_clone 2>&1 | grep "more than one head" &&

# TODO: "more than one head" is the correct response for now, but a more
# appropriate result would be to clone the extra commits, perhaps naming
@@ -47,7 +47,7 @@ test_expect_failure 'anonymous branch from named branch' '
make_hg_commit e test_file &&

cd .. &&
- git clone testgitifyhg::hg_repo git_clone 2>&1 | grep "more than one head" &&
+ git clone gitifyhg::hg_repo git_clone 2>&1 | grep "more than one head" &&
cd git_clone &&
test "`git branch -r`" = " origin/HEAD -> origin/master
origin/branches/featurebranch
diff --git a/test/test_notes.t b/test/test_notes.t
index de4ea19..5319a5f 100755
--- a/test/test_notes.t
+++ b/test/test_notes.t
@@ -54,7 +54,7 @@ test_expect_success 'pull notes rename remote' '
mkdir git_clone &&
cd git_clone &&
git init &&
- git remote add --fetch the_remote testgitifyhg::../hg_repo &&
+ git remote add --fetch the_remote gitifyhg::../hg_repo &&
git pull the_remote master &&
assert_git_messages "a" &&
cd ../hg_repo &&
diff --git a/test/test_pull.t b/test/test_pull.t
index 4367bc3..06347d5 100755
--- a/test/test_pull.t
+++ b/test/test_pull.t
@@ -35,7 +35,7 @@ test_expect_success 'pull named remote' '
mkdir git_repo &&
cd git_repo &&
git init &&
- git remote add --fetch the_remote testgitifyhg::../hg_repo
+ git remote add --fetch the_remote gitifyhg::../hg_repo
git pull the_remote master &&
assert_git_messages a &&
cd ../hg_repo &&
diff --git a/test/test_special_cases.t b/test/test_special_cases.t
index 72ec040..95d3db1 100755
--- a/test/test_special_cases.t
+++ b/test/test_special_cases.t
@@ -38,7 +38,7 @@ test_expect_success 'unicode paths' '
hg add file${SB} &&
hg commit -m ${SB} --user="$HG_USER" &&
cd .. &&
- git clone testgitifyhg::hg${SB}repo git${SB}clone &&
+ git clone gitifyhg::hg${SB}repo git${SB}clone &&
cd git${SB}clone &&
git config user.email $GIT_AUTHOR_EMAIL &&
git config user.name "$GIT_USER"
--
1.9.2+fc1.20.g204a630

Felipe Contreras

unread,
May 3, 2014, 9:55:19 PM5/3/14
to giti...@googlegroups.com, Felipe Contreras
Signed-off-by: Felipe Contreras <felipe.c...@gmail.com>
---
test/test-lib.sh | 2 +-
test/test_push_tags.t | 5 +----
test/test_special_cases.t | 2 +-
3 files changed, 3 insertions(+), 6 deletions(-)

diff --git a/test/test-lib.sh b/test/test-lib.sh
index 69015f9..da9998b 100644
--- a/test/test-lib.sh
+++ b/test/test-lib.sh
@@ -27,7 +27,7 @@ clone_repo() {
test_expect_code 0 git clone "gitifyhg::hg_repo" git_clone &&
cd git_clone &&
git config user.email $GIT_AUTHOR_EMAIL &&
- git config user.name "$GIT_USER"
+ git config user.name "$GIT_AUTHOR_NAME"
}

make_cloned_repo() {
diff --git a/test/test_push_tags.t b/test/test_push_tags.t
index 6ec6833..8f01a6f 100755
--- a/test/test_push_tags.t
+++ b/test/test_push_tags.t
@@ -94,10 +94,7 @@ test_expect_success 'push messaged tag' '
hg tags | grep this_is_a_tag &&
assert_hg_messages "I tagged a message and a user${NL}a" &&
hg log &&
- # FIXME: I feel like this should be $GIT_USER
- # but git seems to be passing me the e-mail twice. Is this a bug in
- # git or something gitifyhg needs to parse?
- assert_hg_author "$GIT_AUTHOR_NAME $GIT_AUTHOR_EMAIL <$GIT_AUTHOR_EMAIL>" &&
+ assert_hg_author "$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>" &&
cd ..
'

diff --git a/test/test_special_cases.t b/test/test_special_cases.t
index 95d3db1..6b3fd4e 100755
--- a/test/test_special_cases.t
+++ b/test/test_special_cases.t
@@ -41,7 +41,7 @@ test_expect_success 'unicode paths' '
git clone gitifyhg::hg${SB}repo git${SB}clone &&
cd git${SB}clone &&
git config user.email $GIT_AUTHOR_EMAIL &&
- git config user.name "$GIT_USER"
+ git config user.name "$GIT_AUTHOR_NAME" &&
assert_git_messages "${SB}" &&

echo ${SB} >> file${SB} &&
--
1.9.2+fc1.20.g204a630

Felipe Contreras

unread,
May 3, 2014, 9:55:20 PM5/3/14
to giti...@googlegroups.com, Felipe Contreras
Commits messages in Git end with a newline.

Signed-off-by: Felipe Contreras <felipe.c...@gmail.com>
---
gitifyhg/hgimporter.py | 2 ++
test/test-lib.sh | 4 ++--
test/test_clone.t | 33 +++++++++++++++++++++++++++++++++
test/test_push.t | 29 +++++++++++++++++++++++++++--
test/test_special_cases.t | 2 +-
5 files changed, 65 insertions(+), 5 deletions(-)

diff --git a/gitifyhg/hgimporter.py b/gitifyhg/hgimporter.py
index 1ff9766..db7a996 100644
--- a/gitifyhg/hgimporter.py
+++ b/gitifyhg/hgimporter.py
@@ -159,6 +159,8 @@ class HGImporter(object):
else:
modified, removed = self.repo[rev].manifest().keys(), []

+ description += '\n'
+
if not parents and rev:
output('reset %s' % gitify_ref)

diff --git a/test/test-lib.sh b/test/test-lib.sh
index da9998b..b853a7c 100644
--- a/test/test-lib.sh
+++ b/test/test-lib.sh
@@ -55,9 +55,9 @@ make_git_commit() {

assert_git_messages() {
if test $# -eq 2 ; then
- test "`git log --pretty=format:%B $2`" = "$1"
+ test "`git log -z --pretty=format:%B $2`" = "$1"
else
- test "`git log --pretty=format:%B`" = "$1"
+ test "`git log -z --pretty=format:%B`" = "$1"
fi
}

diff --git a/test/test_clone.t b/test/test_clone.t
index 8f2b9d5..99903ab 100755
--- a/test/test_clone.t
+++ b/test/test_clone.t
@@ -143,5 +143,38 @@ test_expect_success 'no implicit clone close branch' '
cd ..
'

+test_expect_success 'ensure commit message match' '
+ test_when_finished "rm -rf repo_a repo_b repo_c" &&
+
+ (
+ hg init repo_a &&
+ cd repo_a &&
+ echo "a" >> test_file &&
+ hg add test_file &&
+ hg commit --message="a" &&
+ echo "b" >> test_file &&
+ hg commit --message="b"
+ ) &&
+
+ (
+ git init repo_b &&
+ cd repo_b &&
+ echo "a" >> test_file &&
+ git add test_file &&
+ git commit -a --message="a" &&
+ echo "b" >> test_file &&
+ git commit -a --message="b"
+ ) &&
+
+ git clone "gitifyhg::repo_a" repo_c &&
+
+ printf "%s\n\0" "b" "a" > expected &&
+ git --git-dir=repo_b/.git log -z --format="%B" > actual &&
+ test_cmp expected actual &&
+ git --git-dir=repo_c/.git log -z --format="%B" > actual &&
+ test_cmp expected actual &&
+ hg -R repo_a log --template="{desc}\n\0" > actual &&
+ test_cmp expected actual
+'

test_done
diff --git a/test/test_push.t b/test/test_push.t
index 3bd49b8..00ef669 100755
--- a/test/test_push.t
+++ b/test/test_push.t
@@ -167,7 +167,7 @@ test_expect_success 'fetch after bad push updates master' '
git fetch &&
assert_git_messages "b${NL}a" origin/master &&
git pull --rebase &&
- assert_git_messages "c${NL}${NL}b${NL}a" &&
+ assert_git_messages "c${NL}b${NL}a" &&
git push &&
cd ../hg_repo &&
hg log --template="{desc}\n"
@@ -296,6 +296,31 @@ test_expect_success 'handle paths with quotes' '
cd ..
'

-
+test_expect_success 'simple bidirectional test' '
+ test_when_finished "rm -rf repo_a repo_b repo_c" &&
+
+ (
+ hg init repo_a &&
+ cd repo_a &&
+ echo "a" >> test_file &&
+ hg add test_file &&
+ hg commit --message="a" &&
+ echo "b" >> test_file &&
+ hg commit --message="b"
+ ) &&
+
+ hg init repo_b &&
+
+ (
+ git clone "gitifyhg::repo_a" repo_c &&
+ cd repo_c &&
+ git remote add repo_b gitifyhg::../repo_b &&
+ git push repo_b --all
+ ) &&
+
+ hg -R repo_a log --template="{desc}\n\0" > expected &&
+ hg -R repo_b log --template="{desc}\n\0" > actual &&
+ test_cmp expected actual
+'

test_done
diff --git a/test/test_special_cases.t b/test/test_special_cases.t
index 6b3fd4e..18bcbd3 100755
--- a/test/test_special_cases.t
+++ b/test/test_special_cases.t
@@ -57,7 +57,7 @@ test_expect_success 'unicode paths' '

cd ../git${SB}clone &&
git pull &&
- assert_git_messages "${SB}3${NL}${SB}2${NL}${NL}${SB}" &&
+ assert_git_messages "${SB}3${NL}${SB}2${NL}${SB}" &&

cd ..
'
--
1.9.2+fc1.20.g204a630

Felipe Contreras

unread,
May 3, 2014, 9:55:21 PM5/3/14
to giti...@googlegroups.com, Felipe Contreras
Ported from git-remote-hg.

Fixes #77.

Signed-off-by: Felipe Contreras <felipe.c...@gmail.com>
---
gitifyhg/gitexporter.py | 12 +++++++++++-
test/test_push_tags.t | 6 +-----
2 files changed, 12 insertions(+), 6 deletions(-)

diff --git a/gitifyhg/gitexporter.py b/gitifyhg/gitexporter.py
index b89f4a6..ae853f8 100644
--- a/gitifyhg/gitexporter.py
+++ b/gitifyhg/gitexporter.py
@@ -32,6 +32,9 @@ from distutils.version import StrictVersion
from .util import (die, output, git_to_hg_spaces, hgmode, branch_tip,
ref_to_name_reftype, BRANCH, BOOKMARK, TAG, user_config)

+import subprocess
+import re
+
class dummyui(object):
def debug(self, msg):
pass
@@ -293,7 +296,14 @@ class GitExporter(object):
date_tz = (date, tz)
else:
message = "Added tag %s for changeset %s" % (name, hgshort(node))
- user = self.hgrc.get("ui", "username", None)
+ cmd = ['git', 'var', 'GIT_COMMITTER_IDENT']
+ process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
+ output, _ = process.communicate()
+ m = re.match('^.* <.*>', output)
+ if m:
+ user = m.group(0)
+ else:
+ user = self.hgrc.get("ui", "username", None)
date_tz = None # XXX insert current date here
ctx = memctx(self.repo,
(branch_tip(self.repo, branch), self.NULL_PARENT), message,
diff --git a/test/test_push_tags.t b/test/test_push_tags.t
index 8f01a6f..fb0e341 100755
--- a/test/test_push_tags.t
+++ b/test/test_push_tags.t
@@ -29,14 +29,10 @@ test_expect_success 'push lightweight tag' '
cd ..
'

-# FIXME: See #77
test_expect_success 'lightweight tag sets hg username' '
test_when_finished "rm -rf hg_repo git_clone .hgrc" &&
user="Lite Wait <lite...@example.com>" &&

- # NOTE: sharness set #HOME to the working directory for us, so this is
- # the default hgrc.
- echo "[ui]${NL}username=$user" > .hgrc
make_hg_repo &&
clone_repo &&
git tag "lightweight" &&
@@ -44,7 +40,7 @@ test_expect_success 'lightweight tag sets hg username' '

cd ../hg_repo &&
assert_hg_count 2 &&
- assert_hg_author "$user" &&
+ assert_hg_author "$GIT_USER" &&

cd ..
'
--
1.9.2+fc1.20.g204a630

Felipe Contreras

unread,
May 3, 2014, 9:55:22 PM5/3/14
to giti...@googlegroups.com, Felipe Contreras
Ported from git-remote-hg.

Signed-off-by: Felipe Contreras <felipe.c...@gmail.com>
---
gitifyhg/hgimporter.py | 4 ++--
test/test_clone_file_operations.t | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/gitifyhg/hgimporter.py b/gitifyhg/hgimporter.py
index db7a996..2b60df1 100644
--- a/gitifyhg/hgimporter.py
+++ b/gitifyhg/hgimporter.py
@@ -176,6 +176,8 @@ class HGImporter(object):
if len(parents) > 1:
output("merge :%s" % (self.marks.revision_to_mark(self.repo[parents[1]].node())))

+ for file in removed:
+ output("D %s" % (relative_path(file)))
for file in modified:
filecontext = self.repo[rev].filectx(file)
data = filecontext.data()
@@ -183,8 +185,6 @@ class HGImporter(object):
gitmode(filecontext.flags()), relative_path(filecontext.path())))
output("data %d" % len(data))
output(data)
- for file in removed:
- output("D %s" % (relative_path(file)))
output()

count += 1
diff --git a/test/test_clone_file_operations.t b/test/test_clone_file_operations.t
index b2dc55f..ac6b87f 100755
--- a/test/test_clone_file_operations.t
+++ b/test/test_clone_file_operations.t
@@ -31,7 +31,7 @@ test_expect_success 'cloning a removed file works' '
'

# See issue #36
-test_expect_failure 'cloning a file replaced with a directory' '
+test_expect_success 'cloning a file replaced with a directory' '
test_when_finished "rm -rf hg_repo git_clone" &&

make_hg_repo &&
@@ -49,7 +49,7 @@ test_expect_failure 'cloning a file replaced with a directory' '
'

# also issue #36
-test_expect_failure 'clone replacing a symlink with a directory' '
+test_expect_success 'clone replacing a symlink with a directory' '
test_when_finished "rm -rf hg_repo git_clone" &&

make_hg_repo &&
--
1.9.2+fc1.20.g204a630

Felipe Contreras

unread,
May 3, 2014, 9:55:23 PM5/3/14
to giti...@googlegroups.com, Felipe Contreras
Signed-off-by: Felipe Contreras <felipe.c...@gmail.com>
---
gitifyhg/gitifyhg.py | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/gitifyhg/gitifyhg.py b/gitifyhg/gitifyhg.py
index 4a27f60..1f305ba 100644
--- a/gitifyhg/gitifyhg.py
+++ b/gitifyhg/gitifyhg.py
@@ -24,6 +24,7 @@ import os
import re
import optparse
import subprocess
+import hashlib
from path import path as p

# Enable "plain" mode to make us resilient against changes to the locale, as we
@@ -113,7 +114,10 @@ class GitRemoteParser(object):

class HGRemote(object):
def __init__(self, alias, url):
- if hg.islocal(url.encode('utf-8')):
+ if alias[4:] == url:
+ is_tmp = True
+ alias = hashlib.sha1(alias).hexdigest()
+ elif hg.islocal(url.encode('utf-8')):
url = p(url).abspath()
# Force git to use an absolute path in the future
remote_name = os.path.basename(sys.argv[0]).replace("git-remote-", "")
--
1.9.2+fc1.20.g204a630

Felipe Contreras

unread,
May 3, 2014, 9:55:24 PM5/3/14
to giti...@googlegroups.com, Felipe Contreras
If revisions are stripped fast-import complains about the new branch not
being a descendant of the old one.

Ported from git-remote-hg. Now gitifyhg passes the 'strip' test of
git-remote-hg.

Signed-off-by: Felipe Contreras <felipe.c...@gmail.com>
---
gitifyhg/hgimporter.py | 1 +
1 file changed, 1 insertion(+)

diff --git a/gitifyhg/hgimporter.py b/gitifyhg/hgimporter.py
index 2b60df1..8804f4f 100644
--- a/gitifyhg/hgimporter.py
+++ b/gitifyhg/hgimporter.py
@@ -73,6 +73,7 @@ class HGImporter(object):
output("feature import-marks=%s" % self.hgremote.marks_git_path)
output("feature export-marks=%s" % self.hgremote.marks_git_path)
output("feature notes")
+ output("feature force")

tmp = encoding.encoding
encoding.encoding = 'utf-8'
--
1.9.2+fc1.20.g204a630

Felipe Contreras

unread,
May 3, 2014, 9:55:25 PM5/3/14
to giti...@googlegroups.com, Felipe Contreras
Signed-off-by: Felipe Contreras <felipe.c...@gmail.com>
---
test/test-lib.sh | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/test/test-lib.sh b/test/test-lib.sh
index b853a7c..c5e36da 100644
--- a/test/test-lib.sh
+++ b/test/test-lib.sh
@@ -108,7 +108,7 @@ assert_hg_count() {

assert_git_notes() {
git notes --ref=hg merge $(basename $(ls .git/refs/notes/hg-*)) &&
- git log --pretty="format:%N" --notes='hg' | grep -v '^$'
- echo $1
+ git log --pretty="format:%N" --notes='hg' | grep -v '^$' &&
+ echo $1 &&
test "`git log --pretty="format:%N" --notes='hg' | grep -v '^$'`" = "$1"
}
--
1.9.2+fc1.20.g204a630

Felipe Contreras

unread,
May 3, 2014, 9:55:26 PM5/3/14
to giti...@googlegroups.com, Felipe Contreras
No functional changes.

Signed-off-by: Felipe Contreras <felipe.c...@gmail.com>
---
test/test-lib.sh | 47 +++++++----------------------------------------
1 file changed, 7 insertions(+), 40 deletions(-)

diff --git a/test/test-lib.sh b/test/test-lib.sh
index c5e36da..4805948 100644
--- a/test/test-lib.sh
+++ b/test/test-lib.sh
@@ -37,14 +37,9 @@ make_cloned_repo() {
}

make_hg_commit() {
- if test $# -eq 3 ; then
- user=$3
- else
- user=$HG_USER
- fi
echo "$1" >> $2 &&
hg add $2 &&
- hg commit -m "$1" --user="$user"
+ hg commit -m "$1" --user="${3-$HG_USER}"
}

make_git_commit() {
@@ -54,55 +49,27 @@ make_git_commit() {
}

assert_git_messages() {
- if test $# -eq 2 ; then
- test "`git log -z --pretty=format:%B $2`" = "$1"
- else
- test "`git log -z --pretty=format:%B`" = "$1"
- fi
+ test "`git log -z --pretty=format:%B ${2-}`" = "$1"
}

assert_hg_messages() {
- if test $# -eq 2 ; then
- test "`hg log --template=\"{desc}\n\" -r $2`" = "$1"
- else
- test "`hg log --template=\"{desc}\n\"`" = "$1"
- fi
+ test "`hg log --template=\"{desc}\n\" ${2+-r $2}`" = "$1"
}

assert_hg_author() {
- if test $# -eq 2 ; then
- rev=$2
- else
- rev=tip
- fi
- test "`hg log --template='{author}' --rev=$rev`" = "$1"
+ test "`hg log --template='{author}' --rev=${2-tip}`" = "$1"
}

assert_git_author() {
- if test $# -eq 2 ; then
- ref=$2
- else
- ref=HEAD
- fi
- test "`git show -s --format='%an <%ae>' $ref`" = "$1"
+ test "`git show -s --format='%an <%ae>' ${2-HEAD}`" = "$1"
}

assert_git_count() {
- if test $# -eq 2 ; then
- ref=$2
- else
- ref=HEAD
- fi
- test `git rev-list $ref --count` -eq $1
+ test `git rev-list ${2-HEAD} --count` -eq $1
}

assert_hg_count() {
- if test $# -eq 2 ; then
- rev=$2
- else
- rev=tip
- fi
- test `hg log -q -r 0:$rev | wc -l` -eq $1
+ test `hg log -q -r 0:${2-tip} | wc -l` -eq $1

}

--
1.9.2+fc1.20.g204a630

Felipe Contreras

unread,
May 3, 2014, 9:55:27 PM5/3/14
to giti...@googlegroups.com, Felipe Contreras
Signed-off-by: Felipe Contreras <felipe.c...@gmail.com>
---
test/test-lib.sh | 9 +++++++--
test/test_special_cases.t | 4 ++--
2 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/test/test-lib.sh b/test/test-lib.sh
index 4805948..891aba2 100644
--- a/test/test-lib.sh
+++ b/test/test-lib.sh
@@ -14,12 +14,17 @@ export NL='
export PYTHONPATH="$SHARNESS_BUILD_DIRECTORY"
export PATH="$SHARNESS_TEST_DIRECTORY:$PATH"

+cat > "$HOME"/.hgrc <<EOF
+[ui]
+username = $HG_USER
+EOF
+
make_hg_repo() {
hg init hg_repo &&
cd hg_repo &&
echo 'a\n' >> test_file &&
hg add test_file &&
- hg commit --message="a" --user="$HG_USER"
+ hg commit --message="a"
}

clone_repo() {
@@ -39,7 +44,7 @@ make_cloned_repo() {
make_hg_commit() {
echo "$1" >> $2 &&
hg add $2 &&
- hg commit -m "$1" --user="${3-$HG_USER}"
+ hg commit -m "$1" "${3+--user=$3}"
}

make_git_commit() {
diff --git a/test/test_special_cases.t b/test/test_special_cases.t
index 18bcbd3..f41e90c 100755
--- a/test/test_special_cases.t
+++ b/test/test_special_cases.t
@@ -36,7 +36,7 @@ test_expect_success 'unicode paths' '
echo $SB > file${SB} &&
hg init &&
hg add file${SB} &&
- hg commit -m ${SB} --user="$HG_USER" &&
+ hg commit -m ${SB} &&
cd .. &&
git clone gitifyhg::hg${SB}repo git${SB}clone &&
cd git${SB}clone &&
@@ -53,7 +53,7 @@ test_expect_success 'unicode paths' '
assert_hg_messages "${SB}2${NL}${SB}" &&

echo ${SB} >> file${SB} &&
- hg commit -m "${SB}3" --user="$HG_USER" &&
+ hg commit -m "${SB}3" &&

cd ../git${SB}clone &&
git pull &&
--
1.9.2+fc1.20.g204a630

Felipe Contreras

unread,
May 3, 2014, 9:55:28 PM5/3/14
to giti...@googlegroups.com, Felipe Contreras
Signed-off-by: Felipe Contreras <felipe.c...@gmail.com>
---
test/test-lib.sh | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/test/test-lib.sh b/test/test-lib.sh
index 891aba2..3455a81 100644
--- a/test/test-lib.sh
+++ b/test/test-lib.sh
@@ -42,8 +42,8 @@ make_cloned_repo() {
}

make_hg_commit() {
- echo "$1" >> $2 &&
- hg add $2 &&
+ echo "$1" >> "$2" &&
+ hg add "$2" &&
hg commit -m "$1" "${3+--user=$3}"
}

--
1.9.2+fc1.20.g204a630

Felipe Contreras

unread,
May 3, 2014, 9:55:29 PM5/3/14
to giti...@googlegroups.com, Felipe Contreras
We want to have both 'branches/default' and 'master'.

It makes the code more straight-forward.

The reason make_gitify_ref() needs to return bookmarks/master is because
that's what we told 'git fast-import' we would use for refs/heads/master
through the refspec.

Signed-off-by: Felipe Contreras <felipe.c...@gmail.com>
---
gitifyhg/gitifyhg.py | 13 ++++---------
gitifyhg/hgimporter.py | 20 ++++++++++++--------
gitifyhg/util.py | 5 +----
test/test_bookmarks.t | 3 +++
test/test_clone.t | 3 +++
test/test_spaces.t | 2 ++
6 files changed, 25 insertions(+), 21 deletions(-)

diff --git a/gitifyhg/gitifyhg.py b/gitifyhg/gitifyhg.py
index 1f305ba..31e438a 100644
--- a/gitifyhg/gitifyhg.py
+++ b/gitifyhg/gitifyhg.py
@@ -180,15 +180,7 @@ class HGRemote(object):
if not isinstance(name, unicode):
name = name.decode('utf-8')
if reftype == BRANCH:
- if name == 'default':
- # I have no idea where 'bookmarks' comes from in this case.
- # I don't think there is meant to be many bookmarks/master ref,
- # but this is what I had to do to make tests pass when special
- # casing the master/default dichotomy. Something is still fishy
- # here, but it's less fishy than it was. See issue #34.
- return "%s/bookmarks/master" % self.prefix
- else:
- return '%s/branches/%s' % (self.prefix, name)
+ return '%s/branches/%s' % (self.prefix, name)
elif reftype == BOOKMARK:
return '%s/bookmarks/%s' % (self.prefix, name)
elif reftype == TAG:
@@ -287,6 +279,9 @@ class HGRemote(object):

# list the named branch references
for branch in self.branches:
+ if branch == 'default':
+ output("%s %s" %
+ (self._change_hash(branch_head(self, branch)), "refs/heads/master"))
output("%s %s" %
(self._change_hash(branch_head(self, branch)),
name_reftype_to_ref(hg_to_git_spaces(branch), BRANCH)))
diff --git a/gitifyhg/hgimporter.py b/gitifyhg/hgimporter.py
index 8804f4f..1edbde2 100644
--- a/gitifyhg/hgimporter.py
+++ b/gitifyhg/hgimporter.py
@@ -88,15 +88,19 @@ class HGImporter(object):
BOOKMARK,
self.hgremote.headnode[1])
else:
- name, reftype = ref_to_name_reftype(ref)
- if reftype == BRANCH:
- head = branch_head(self.hgremote, git_to_hg_spaces(name))
- elif reftype == BOOKMARK:
- head = self.hgremote.bookmarks[git_to_hg_spaces(name)]
- elif reftype == TAG:
- head = self.repo[git_to_hg_spaces(name)]
+ if ref == 'refs/heads/master':
+ name, reftype = 'master', BOOKMARK
+ head = branch_head(self.hgremote, 'default')
else:
- assert False, "unexpected reftype: %s" % reftype
+ name, reftype = ref_to_name_reftype(ref)
+ if reftype == BRANCH:
+ head = branch_head(self.hgremote, git_to_hg_spaces(name))
+ elif reftype == BOOKMARK:
+ head = self.hgremote.bookmarks[git_to_hg_spaces(name)]
+ elif reftype == TAG:
+ head = self.repo[git_to_hg_spaces(name)]
+ else:
+ assert False, "unexpected reftype: %s" % reftype
self.process_ref(name, reftype, head)

self.process_notes()
diff --git a/gitifyhg/util.py b/gitifyhg/util.py
index afb8030..7bc4b89 100644
--- a/gitifyhg/util.py
+++ b/gitifyhg/util.py
@@ -133,10 +133,7 @@ def ref_to_name_reftype(ref):
def name_reftype_to_ref(name, reftype):
'''Converts a name and type (e.g., '1.0' and 'tags') into a git ref.'''
if reftype == BRANCH:
- if name == 'default':
- return 'refs/heads/master'
- else:
- return 'refs/heads/branches/%s' % name
+ return 'refs/heads/branches/%s' % name
elif reftype == BOOKMARK:
return 'refs/heads/%s' % name
elif reftype == TAG:
diff --git a/test/test_bookmarks.t b/test/test_bookmarks.t
index 0a5df0d..47cd6ad 100755
--- a/test/test_bookmarks.t
+++ b/test/test_bookmarks.t
@@ -23,6 +23,7 @@ test_expect_success 'clone bookmark' '
clone_repo &&

test "`git branch -r`" = " origin/HEAD -> origin/master
+ origin/branches/default
origin/featurebookmark
origin/master" &&

@@ -49,6 +50,7 @@ test_expect_success 'clone divergent bookmarks' '
test "`git branch -r`" = " origin/HEAD -> origin/master
origin/bookmark_one
origin/bookmark_two
+ origin/branches/default
origin/master" &&

git checkout origin/bookmark_one &&
@@ -72,6 +74,7 @@ test_expect_success 'clone bookmark not at tip' '

test "`git branch -r`" = " origin/HEAD -> origin/master
origin/bookmark_one
+ origin/branches/default
origin/master" &&

git checkout bookmark_one &&
diff --git a/test/test_clone.t b/test/test_clone.t
index 99903ab..636c8c5 100755
--- a/test/test_clone.t
+++ b/test/test_clone.t
@@ -33,6 +33,7 @@ test_expect_success 'clone linear branch, no multiple parents' '
clone_repo &&
assert_git_messages "a" &&
test "`git branch -r`" = " origin/HEAD -> origin/master
+ origin/branches/default
origin/branches/featurebranch
origin/master" &&

@@ -112,6 +113,7 @@ test_expect_success 'clone close branch' '

clone_repo &&
test "`git branch -r`" = " origin/HEAD -> origin/master
+ origin/branches/default
origin/branches/feature
origin/master" &&
assert_git_messages "c${NL}a" &&
@@ -137,6 +139,7 @@ test_expect_success 'no implicit clone close branch' '
clone_repo &&
git branch -r &&
test "`git branch -r`" = " origin/HEAD -> origin/master
+ origin/branches/default
origin/master" &&
assert_git_messages "c${NL}a" &&

diff --git a/test/test_spaces.t b/test/test_spaces.t
index c3ed0a8..ea4bd1c 100755
--- a/test/test_spaces.t
+++ b/test/test_spaces.t
@@ -22,6 +22,7 @@ test_expect_success 'clone branch with spaces' '
clone_repo &&
assert_git_messages "a" &&
test "`git branch -r`" = " origin/HEAD -> origin/master
+ origin/branches/default
origin/branches/feature___branch
origin/master" &&

@@ -42,6 +43,7 @@ test_expect_success 'clone bookmark with spaces' '
clone_repo

test "`git branch -r`" = " origin/HEAD -> origin/master
+ origin/branches/default
origin/feature___bookmark
origin/master" &&

--
1.9.2+fc1.20.g204a630

Felipe Contreras

unread,
May 3, 2014, 9:55:30 PM5/3/14
to giti...@googlegroups.com, Felipe Contreras
Signed-off-by: Felipe Contreras <felipe.c...@gmail.com>
---
gitifyhg/gitexporter.py | 2 --
test/test_push_tags.t | 4 ++--
2 files changed, 2 insertions(+), 4 deletions(-)

diff --git a/gitifyhg/gitexporter.py b/gitifyhg/gitexporter.py
index ae853f8..dfa3238 100644
--- a/gitifyhg/gitexporter.py
+++ b/gitifyhg/gitexporter.py
@@ -283,8 +283,6 @@ class GitExporter(object):

encoded_tag = encoding.fromlocal(name)
tag_line = '%s %s' % (hghex(node), encoded_tag)
- if tag_line in old_tags:
- return # Don't commit a tag that was previously committed
newtags.append(tag_line)

def get_filectx(repo, memctx, file):
diff --git a/test/test_push_tags.t b/test/test_push_tags.t
index fb0e341..d92e4c3 100755
--- a/test/test_push_tags.t
+++ b/test/test_push_tags.t
@@ -73,7 +73,7 @@ test_expect_success 'push tag with previous commits' '

cd ../hg_repo &&
hg tags | grep this_is_a_tag &&
- assert_hg_messages "Added tag this_is_a_tag for changeset $(hg id --id -r 2)${NL}b${NL}Added tag an_old_tag for changeset $hgsha1${NL}a" &&
+ assert_hg_messages "Added tag an_old_tag for changeset $hgsha1${NL}Added tag this_is_a_tag for changeset $(hg id --id -r 2)${NL}b${NL}Added tag an_old_tag for changeset $hgsha1${NL}a" &&

cd ..
'
@@ -127,7 +127,7 @@ test_expect_success 'push only new tag' '
test `hg tags | wc -l` -eq 3 && # 3 is tip
hg tags | grep this_is_a_tag &&
hg tags | grep an_old_tag &&
- assert_hg_count 3 &&
+ assert_hg_count 4 &&

cd ..
'
--
1.9.2+fc1.20.g204a630

Felipe Contreras

unread,
May 3, 2014, 9:55:31 PM5/3/14
to giti...@googlegroups.com, Felipe Contreras
Signed-off-by: Felipe Contreras <felipe.c...@gmail.com>
---
gitifyhg/hgimporter.py | 32 ++++++++++++++++++--------------
test/test_author.t | 6 +++---
2 files changed, 21 insertions(+), 17 deletions(-)

diff --git a/gitifyhg/hgimporter.py b/gitifyhg/hgimporter.py
index 1edbde2..dd0e30f 100644
--- a/gitifyhg/hgimporter.py
+++ b/gitifyhg/hgimporter.py
@@ -27,32 +27,36 @@ from .util import (log, output, gittz, gitmode,
git_to_hg_spaces, hg_to_git_spaces, branch_head, ref_to_name_reftype,
BRANCH, BOOKMARK, TAG, relative_path)

-AUTHOR = re.compile(r'^([^<>]+)?(<(?:[^<>]*)>| [^ ]*@.*|[<>].*)$')
-
+AUTHOR_RE = re.compile(r'^([^<>]+?)? ?[<>]([^<>]*)(?:$|>)')
+EMAIL_RE = re.compile(r'([^ \t<>]+@[^ \t<>]+)')
+NAME_RE = re.compile('^([^<>]+)')

def sanitize_author(author):
'''Mercurial allows a more freeform user string than git, so we have to
massage it to be compatible. Git expects "name <email>", where email can be
empty (as long as it's surrounded by <>).'''
- name = ''
- email = ''
+
+ name = mail = ''
author = author.replace('"', '')
- match = AUTHOR.match(author)
- if match:
- if match.group(1): # handle 'None', e.g for input "<only@email>"
- name = match.group(1).strip()
- email = match.group(2).translate(None, "<>").strip()
+ m = AUTHOR_RE.match(author)
+ if m:
+ name = m.group(1)
+ mail = m.group(2).strip()
else:
- author = author.translate(None, "<>").strip()
- if "@" in author:
- email = author
+ m = EMAIL_RE.match(author)
+ if m:
+ mail = m.group(1)
else:
- name = author
+ m = NAME_RE.match(author)
+ if m:
+ name = m.group(1).strip()

if not name:
name = 'Unknown'
+ if not mail:
+ mail = 'unknown'

- return "%s <%s>" % (name, email)
+ return "%s <%s>" % (name, mail)


class HGImporter(object):
diff --git a/test/test_author.t b/test/test_author.t
index 7126b2c..0e47edb 100755
--- a/test/test_author.t
+++ b/test/test_author.t
@@ -55,7 +55,7 @@ test_expect_success 'author no email' '
make_hg_commit b test_file "no email supplied" &&

clone_repo &&
- assert_git_author "no email supplied <>" &&
+ assert_git_author "no email supplied <unknown>" &&

cd ..
'
@@ -104,7 +104,7 @@ test_expect_success 'author no email quoting' '
make_hg_commit b test_file "no email quoting em...@example.com" &&

clone_repo &&
- assert_git_author "no email quoting <em...@example.com>" &&
+ assert_git_author "no email quoting em...@example.com <unknown>" &&

cd ..
'
@@ -141,7 +141,7 @@ test_expect_success 'author abuse quotes' '
make_hg_commit b test_file "totally >>> bad <<< quote can be used in hg <><><" &&

clone_repo &&
- assert_git_author "totally <bad quote can be used in hg>" &&
+ assert_git_author "totally <unknown>" &&

cd ..
'
--
1.9.2+fc1.20.g204a630

Reply all
Reply to author
Forward
0 new messages