104 Commits

Author SHA1 Message Date
Mark Nauwelaerts
aadc899982 Release v1.0.3.1 2022-02-22 22:00:48 +01:00
Paul Wise
4d38bff053 Fail the build when Python with Mercurial is not available 2022-02-22 21:58:51 +01:00
Paul Wise
a8cd6a92b3 Do not overwrite found python executable with 'python' 2022-02-22 21:58:51 +01:00
Paul Wise
a08ad9d2b4 Add an option to test the pre-installed scripts
This is useful for distros like Debian that do tests on installed packages.

See-also: https://ci.debian.net/
2022-02-22 21:58:51 +01:00
Mark Nauwelaerts
8c08b6de21 Release v1.0.3 2022-02-21 22:55:11 +01:00
Mark Nauwelaerts
949345fb11 test: drop check on default
... since default branch is updated semi-random (implementation defined) from
the last non-branch push.

Fixes mnauw/git-remote-hg#48
2022-02-20 15:38:54 +01:00
Mark Nauwelaerts
0d49f75131 test: additional basic configuration 2022-02-20 15:38:54 +01:00
Mark Nauwelaerts
7159e4a030 Ensure URL encoding of tilde when sanitizing refname
Fixes mnauw/git-remote-hg#44
2021-08-28 20:29:59 +02:00
Paul Wise
cdcd70b453 Tell git to ignore the Python setuptools build/dist/metadata dirs
This helps ensure these don't get committed by accident and
helps prevent git interacting with them in other ways.
2021-08-22 21:14:24 +02:00
Paul Wise
f5c38f3a59 Run Python scripts under the chosen version
The Python scripts have unversioned Python shebangs so tests will
fail if the chosen Python version is not unversioned Python.

Copy the scripts to tmp dirs and change the shebang to the chosen Python.

Also prevent sharness from adding the unversioned Python scripts to PATH.
2021-04-05 13:27:00 +02:00
Paul Wise
3b11156e69 Detect which Python version is available and run tests using it
Allow overriding the Python version.

Use importing the mercurial module as the test rather than just python,
and merge the python and mercurial module skipping into one test for both
since the mercurial module has to work under the chosen python version.
2021-04-05 13:27:00 +02:00
Paul Wise
afc1f3a2c2 Try to install Python scripts with versioned Python shebangs
Allow overriding the Python version.

The Python setuptools based build system already modifies shebangs.

The Python scripts have unversioned Python shebangs so they
will fail if an unversioned Python is not provided by the OS.

The Debian bullseye release will not have an unversioned Python by default.

Avoid make pattern rules and shell functions since they are not portable.
2021-04-05 13:27:00 +02:00
Paul Wise
e596a5f457 Add missing line ending at EOF 2021-04-05 13:25:36 +02:00
Paul Wise
ec654d4682 Be more flexible about the path to sharness
Some folks might want to run a different version.

Check all the common installation paths and
support using a custom path to sharness.
2021-04-03 10:29:48 +02:00
Paul Wise
7913920a97 Create temporary directories dynamically
This avoids creating them in /tmp when another tmp dir was chosen.
2021-04-03 10:03:10 +02:00
Paul Wise
da60201ae3 Clean up temporary directories on exit 2021-04-03 10:03:10 +02:00
Paul Wise
704869df29 Install git-hg-helper when using the make build system
It is used by git-remote-hg and the Python
also setuptools build system installs it.
2021-04-03 10:02:49 +02:00
Paul Wise
5769e965eb Expect success for the main pull tags test
The git bug that caused the switch to unstable has been fixed.

This reverts commit a5dfc9025b.
2021-04-03 10:01:35 +02:00
Paul Wise
1ee28bd233 Use a compatible umask for the executable bit preservation test
More restrictive umasks like 027 will cause the test to fail.

Ubuntu 21.04 will change the default umask to 027.
2021-04-03 10:00:58 +02:00
Paul Wise
1796289df3 Remove unused Python imports in the setuptools build system
Suggested-by: vulture
2021-04-03 10:00:18 +02:00
Mark Nauwelaerts
929ae262f5 Release v1.0.2.1 2020-08-03 21:07:22 +02:00
Mark Nauwelaerts
4328aa1c19 setup: add python3 as supported version 2020-08-03 21:07:22 +02:00
Mark Nauwelaerts
919678be4a Release v1.0.2 2020-07-19 19:18:17 +02:00
Mark Nauwelaerts
ac8f659620 Really use generic python reference in shebang 2020-07-19 19:08:31 +02:00
Mark Nauwelaerts
28ed63b707 test: use generic python reference rather than python2 2020-06-12 14:00:17 +02:00
Mark Nauwelaerts
dc91c58e1c Use generic python reference in shebang 2020-06-12 14:00:17 +02:00
Mark Nauwelaerts
46178f546a test: use a legacy mode for hg-git test 2020-06-12 13:42:19 +02:00
Mark Nauwelaerts
4c0e8e6439 Make tag writing more robust 2020-06-12 13:42:19 +02:00
Mark Nauwelaerts
59b5a8c848 Make python2/3 compatible
See mnauw/git-remote-hg#27
2020-06-12 13:26:45 +02:00
Mark Nauwelaerts
be2445963b test: align order of options to stable ArgumentParser expectation 2020-06-12 13:20:25 +02:00
Mark Nauwelaerts
8b4bfe7e87 test: drop check on master in push mode
... since master is picked semi-random (implementation defined) in case
of multiple default branch tips

This mainly affects push mode since a preceding command succeeds
partially (and therefore affect the random), whereas the preceding fails
completely in non-push mode.
2020-06-12 13:20:25 +02:00
Mark Nauwelaerts
741b440fcc Apply url encoding for additional refname sanitizing
Fixes mnauw/git-remote-hg#33
2020-06-12 13:20:25 +02:00
Mark Nauwelaerts
03b1ac9845 Correctly check for valid revision
Fixes mnauw/git-remote-hg#32
2020-06-12 13:20:25 +02:00
Mark Nauwelaerts
f3a8546406 Merge branch 'felipec'
Integrate changes from felipec where appropriate and still relevant.
2020-06-01 13:02:48 +02:00
Mark Nauwelaerts
ccc9e55b7e Merge remote-tracking branch 'felipec/master' into felipec
These remaining changes have been mostly superseded
(e.g. revwalk iso gitrange, no more fetch-first error).
2020-06-01 12:58:26 +02:00
Mark Nauwelaerts
b516aa9326 Merge commit 'b3cd' into felipec
These changes can be merged with limited to none conflict resolution.
2020-06-01 12:57:26 +02:00
Mark Nauwelaerts
08626200d0 Merge commit '7fb9' into felipec
An unattributed variation of existing commits that can be discarded.
2020-06-01 12:54:44 +02:00
Mark Nauwelaerts
b60eb47173 Merge commit 'fad59f' into felipec
These changes can be merged with limited to none conflict resolution.
2020-06-01 12:52:41 +02:00
Mark Nauwelaerts
76162ce148 Merge commit '4d33' into felipec
These changes are unattributed ports of existing fixes and can as such
mostly be discarded.
2020-06-01 11:53:36 +02:00
Mark Nauwelaerts
7ae03f7640 Merge commit '5cc27' into felipec
These changes can be merged with limited to none conflict resolution.
2020-06-01 11:38:52 +02:00
Mark Nauwelaerts
95da53badd test: adjust push force to push mode behavior 2020-06-01 11:12:50 +02:00
Mark Nauwelaerts
a0608664ca Remove commented remnant 2020-06-01 11:12:34 +02:00
David Turner
9d6d135855 fix race fix on local repos 2019-08-03 21:23:51 +02:00
David Turner
60a6c7b36d Fix a race condition
If new heads are created on a remote http repo after we pull but
before we request branch heads, we'll try to read a head that we don't
actually have locally.  To fix this, we request the branchmap before
fetching, and only fetch the heads that we just learned about.
2019-07-22 21:22:09 +02:00
Felipe Contreras
74d1aa14ac gitrange(): general refactoring
To make the code more readable.

No functional changes.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-20 14:39:36 -05:00
Felipe Contreras
1d85449b0b gitrange(): always check negatives first
Also, always add the parents as negatives.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-20 14:36:21 -05:00
Felipe Contreras
fe8b8c1a61 gitrange(): store parentrevs method
By calling the methods through a variable the code is significantly
faster.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-20 14:36:07 -05:00
Felipe Contreras
510441bba9 gitrange(): add a == b check
Otherwise we return the whole repository. Thanks to the marks we don't
actually export it, but there's no need to return so many revs.

Reported-by: Mark Nauwelaerts <mnauw@users.sourceforge.net>
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-20 14:35:45 -05:00
Mark Nauwelaerts
fa3484e08b Find outgoing changesets earlier
This way we can find if we actually need to push something.

Recent versions of Mercurial already handle this correctly, but let's
check ourselves to make sure, and make it work with all versions.

Rewritten-by: Felipe Contreras <felipe.contreras@gmail.com>
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-19 10:12:05 -05:00
Mark Nauwelaerts
d1544e2ccd test: pushing a bookmark without changesets
This works in recent versions of Mercurial.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-19 09:58:21 -05:00
Felipe Contreras
153a216f47 test: main: cleanup big push fetch first
In order to use setup_big_push().

No functional changes.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-18 19:24:19 -05:00
Felipe Contreras
b3cdbe8e96 Allow --force --dry-run
No reason for it not to work.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-18 18:50:52 -05:00
Felipe Contreras
d11509cab7 test: main: trivial cleanup
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-18 18:45:03 -05:00
Felipe Contreras
4d01165b1b test: main: fix check_push() bug
We were not checking the kind correctly. Add a new case switch to avoid
these issues.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-18 18:18:00 -05:00
Felipe Contreras
a030d603ac test: main: cleanup check_push()
Return immediately after a failure.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-18 18:11:05 -05:00
Felipe Contreras
3d4a5a6d7e Trivial cleanup
By adding a helper function the code is more understandable.

No functional changes.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-18 15:22:09 -05:00
Felipe Contreras
7fb9d9b642 Load global UI confguration
Since 4.1 there's a separate function for that.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-18 15:06:39 -05:00
Felipe Contreras
fad59f53eb Refactor timezone functions
Split them into hours and minutes, and use divmod() to make them more
readable.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-05 03:17:39 -05:00
Oliver Stueker
e17e147fb1 Fix timezone issue with negative offsets
The current code miscalculates the time-zone offsets for time zones that don't
have a full-hour offset and are located west of UTC (e.g. St. John's,
Newfoundland).

Basically it's caused because 33 // 10 == 3, but -33 // 10 != -3.

Take the example of St. John's (-0330). The correct integer timezone should be
3.5 * 3600 (12600), however, since we are not checking for negative module
arithmetic, instead of calculating the timezone for (-3, -30), we are doing it
for (-4, 70), which would be OK... if we had hours of 100 minutes:

  -4 * 100 + 70 = -330

We could fix the code to use proper negative arithmetic (mod -100), but why
bother with the extra complexity? Let's just use absolute numbers and fix the
sign later.

This fixes issue #48.

Commit message written by Felipe Contreras.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-05 03:12:47 -05:00
Felipe Contreras
4878023a8b Add helper for hg timezone
No functional changes.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-04 15:30:38 -05:00
Oliver Stueker
0dfae24d21 test: add test for issue with negative offsets
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-04 15:30:36 -05:00
Felipe Contreras
c7dbb5612b Add timezone bidirectionality test
Oliver Stueker pointed out correctly that there is an issue with the way
we handle negative timestamps that don't have a full hour offset.

This test shows that.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-04 15:28:35 -05:00
Felipe Contreras
cc4e5659d9 Merge branch 'check-versions'
* check-versions:
  travis: use check-versions tool
  check-versions: add hack for hg-git 0.8.x
  Add tool to run tests on many versions
2019-06-04 04:02:38 -05:00
Felipe Contreras
2c993b3433 travis: use check-versions tool
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-04 04:02:12 -05:00
Felipe Contreras
4944a384cd check-versions: add hack for hg-git 0.8.x
They made a mistake and broke bidirectionality when debugextrainmessage
is used.

The upstream report:

  https://bitbucket.org/durin42/hg-git/issues/281/

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-04 04:02:12 -05:00
Felipe Contreras
f29c42c645 Add tool to run tests on many versions
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-04 04:02:12 -05:00
Felipe Contreras
7b7c65f72d README: remove old limitations
They work now.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-04 04:02:06 -05:00
Felipe Contreras
cee3ed7c00 travis: add pip cache
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-04 00:56:41 -05:00
Felipe Contreras
01c9a981c7 travis: install latest released version of hg
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-04 00:18:18 -05:00
Felipe Contreras
d0a5888580 travis: update Mercurial versions
And general cleanups.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-04 00:12:47 -05:00
Felipe Contreras
a5dfc9025b test: mark a regression in Git
The issue is reported, and a proposed fix has been sent:

  https://marc.info/?l=git&m=155961441427321

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-03 21:32:05 -05:00
Felipe Contreras
4d337cff06 Don't catch all exceptions in export_ref()
Just in case.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-03 20:23:18 -05:00
Felipe Contreras
5cc271ef18 test: hggit: add simple file rename test
The Hg-Git project has put a lot of emphasis on file renaming, better
check for that explicitly, even though we are already testing that.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-03 20:23:18 -05:00
Felipe Contreras
c8fff2cd06 Use changelog.isancestorrev()
The old changelog.descendant() is deprecated in 4.7, gone in 4.8.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-03 20:23:18 -05:00
Felipe Contreras
c95fba3c18 Don't call repo[tag] directly
Deprecated in 4.6, gone in 4.7.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-03 20:23:18 -05:00
Felipe Contreras
aaef56a2a3 Use marks.applychange()
Since 4.3 bookmarks are updated with applychanges() and since 4.6
anything else is deprecated.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-03 20:23:18 -05:00
Felipe Contreras
a16c69a99c Refactor updatebookmarks()
Also, use the proper lock, even for older versions.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-03 20:23:18 -05:00
Felipe Contreras
00e95fd8df Call node.rev() instead of int(node)
The int(node) method has been removed in 4.6.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-03 20:23:18 -05:00
Felipe Contreras
5bf7aad6e3 Fix memfilectx() params
These changed again in 4.5.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-03 20:23:18 -05:00
Felipe Contreras
9b8e0ec2c0 Refactor memfilectx() method
It's getting hairy.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-03 20:23:18 -05:00
Felipe Contreras
b309562574 Use makechangegroup()
Since 4.4 we have yet another way to push revisions.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-03 20:23:18 -05:00
Felipe Contreras
e25d3d78cd Use repo.vfs() instead of repo.opener()
The method() opener has been a link to vfs() since a long time, and it's
now removed in 4.3.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-03 20:23:18 -05:00
Felipe Contreras
ed5a70706a Fix getchangegroup() for 4.0
The arguments heads and common have been removed.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-03 20:23:18 -05:00
Felipe Contreras
0faf2c9189 Tell hg-git to be backwards compatible
Since version 0.7.0 hg-git stores extra information directly into the
commits, we don't support that, so we need to tell hg-git to do what it
always did: put the extra data in the message.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-03 19:54:48 -05:00
Felipe Contreras
ada49422a7 test: add test for annotated tags
So we make sure they keep working as expected.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-03 19:54:39 -05:00
Felipe Contreras
580cea0d31 Explicitly process tags
Normally tag commands come with a corresponding ref, but not since
Git v2.21.

It's not clear if Git's change is correct, but fix it our end anyway.

  fdf31b6369 (fast-export: ensure we export requested refs)

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-03 19:51:38 -05:00
Felipe Contreras
1f376e437f Revert "test: skip tests with broken hg-git compatibility"
We need to fix the compatibility.

This reverts commit 29a0d8a0e3.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-03 19:51:35 -05:00
Felipe Contreras
4108665799 Add version check for filelog creation
The functions are only present in 3.2. Older versions don't need this
code.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-03 19:49:54 -05:00
Felipe Contreras
fc28115a53 Avoid ManifestLookupError
In versions older than 2.6 ManifestLookupError doesn't exist.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-03 19:49:08 -05:00
Felipe Contreras
e3009683f8 test: add missing &&s
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-03 14:55:25 -05:00
Felipe Contreras
54cec85f94 test: update expected result
It seems these work since Git v2.0.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-03 14:55:20 -05:00
Felipe Contreras
5ad322c54d Merge branch 'sharness-output-dir'
* sharness-output-dir:
  test: sharness: add support for output directory
2019-06-03 14:54:51 -05:00
Felipe Contreras
13bbc8a342 test: sharness: add support for output directory
I don't know why this was removed from Git's version.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-03 14:54:44 -05:00
Felipe Contreras
673b50d3f4 test: update to sharness 1.1
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
2019-06-03 14:54:29 -05:00
Mark Nauwelaerts
35ecb45fda Tweak hg to git ref name translation
Fixes mnauw/git-remote-hg#26
2019-05-19 13:01:59 +02:00
Mark Nauwelaerts
1456e68129 Really strip prefix to ensure a valid package version 2019-04-28 14:32:42 +02:00
Mark Nauwelaerts
de95133416 Release v1.0.1 2019-04-27 15:08:20 +02:00
Mark Nauwelaerts
e0b752be8f Move release version management to make stage
... to ensure setup.py does not trip some later time.

Fixes mnauw/git-remote-hg#25
2019-04-27 15:08:20 +02:00
native-api
f050de1bcc Additional installation step in Windows
fixes https://github.com/mnauw/git-remote-hg/issues/23
2019-03-19 20:41:21 +01:00
Mark Nauwelaerts
0bf3db826b Handle platform dependency in atomic file renaming 2019-01-06 15:51:42 +01:00
Mark Nauwelaerts
3698638e98 Always pass forward slash to git
Fixes mnauw/git-remote-hg#22
2019-01-06 15:51:42 +01:00
Mark Nauwelaerts
765f9ae287 Add python pip packaging
Fixes mnauw/git-remote-hg#13
2018-10-14 16:27:51 +02:00
Mark Nauwelaerts
5ddcdd33ec Adjust to Mercurial 4.7 wrt deprecated revlog method 2018-09-09 12:08:29 +02:00
Mark Nauwelaerts
435373ee83 Adjust to Mercurial 4.7 wrt more restricted changectx API
Fixes mnauw/git-remote-hg#18
2018-09-09 12:08:26 +02:00
19 changed files with 1811 additions and 686 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
/build/
/dist/
/git_remote_hg.egg-info/

View File

@@ -1,28 +1,20 @@
language: python
dist: xenial
language: minimal
install:
- if [ "$HG_VERSION" != "dev" ];
then pip install -q Mercurial${HG_VERSION+==$HG_VERSION};
else pip install -q http://selenic.com/repo/hg/archive/tip.tar.gz;
fi
- pip install -q dulwich hg-git==0.6.1 || true
before_script:
- hg --version || true
- pip show hg-git dulwich
cache:
directories:
- $HOME/.cache/git-remote-hg
script:
- make test
- ./tools/check-versions hg:$HG_VERSION
matrix:
include:
- env: HG_VERSION=2.9.1
- env: HG_VERSION=2.8.2
- env: HG_VERSION=2.7.2
- env: HG_VERSION=3.0
- env: HG_VERSION=3.5.2
- env: HG_VERSION=3.6.3
- env: HG_VERSION=3.7
- env: HG_VERSION=dev
- python: 2.7
- python: 2.6
- env:
- env: HG_VERSION=@
- env: HG_VERSION=5.0
- env: HG_VERSION=4.9
- env: HG_VERSION=4.8
- env: HG_VERSION=4.7
- env: HG_VERSION=4.6
- env: HG_VERSION=4.5

View File

@@ -3,7 +3,28 @@ prefix := $(HOME)
bindir := $(prefix)/bin
mandir := $(prefix)/share/man/man1
all: doc
all: build doc
build:
if [ -n "$$PYTHON" ] && "$$PYTHON" -c 'import mercurial' 2> /dev/null ; then \
: Use chosen Python version ; \
elif python3 -c 'import mercurial' 2> /dev/null ; then \
PYTHON=python3 ; \
elif python2 -c 'import mercurial' 2> /dev/null ; then \
PYTHON=python2 ; \
elif python -c 'import mercurial' 2> /dev/null ; then \
PYTHON=python ; \
else ; \
echo 'Python with Mercurial not available' >&2 ; \
exit 1 ; \
fi ; \
mkdir -p bin ; \
for s in git-remote-hg git-hg-helper ; do \
printf "%s\n" "#!/usr/bin/env $$PYTHON" > "bin/$$s" ; \
tail -n +2 "./$$s" >> "bin/$$s" ; \
chmod 755 "bin/$$s" ; \
touch -r "./$$s" "bin/$$s" ; \
done
doc: doc/git-remote-hg.1
@@ -15,15 +36,29 @@ doc/git-remote-hg.1: doc/git-remote-hg.txt
clean:
$(RM) doc/git-remote-hg.1
$(RM) -r bin/
D = $(DESTDIR)
install:
install: build
install -d -m 755 $(D)$(bindir)/
install -m 755 git-remote-hg $(D)$(bindir)/git-remote-hg
install -m 755 bin/git-remote-hg $(D)$(bindir)/git-remote-hg
install -m 755 bin/git-hg-helper $(D)$(bindir)/git-hg-helper
install-doc: doc
install -d -m 755 $(D)$(mandir)/
install -m 644 doc/git-remote-hg.1 $(D)$(mandir)/git-remote-hg.1
.PHONY: all test install install-doc clean
pypi:
version=`git describe --tags ${REV}` && \
sed -i "s/version = .*/version = '$$version'[1:]/" setup.py
-rm -rf dist build
python setup.py sdist bdist_wheel
pypi-upload:
twine upload dist/*
pypi-test:
twine upload --repository-url https://test.pypi.org/legacy/ dist/*
.PHONY: all build test install install-doc clean pypy pypy-upload

View File

@@ -13,6 +13,13 @@ wget https://raw.github.com/mnauw/git-remote-hg/master/git-remote-hg -O ~/bin/gi
chmod +x ~/bin/git-remote-hg
--------------------------------------
In Windows, you also need to put and an NTFS symbolic link named `python2.exe` somewhere
on your `$PATH` pointing to your Python 2 executable:
--------------------------------------
mklink <path to link> <path to python.exe>
--------------------------------------
That's it :)
Obviously you will need Mercurial installed.
@@ -146,8 +153,6 @@ Limitations of the remote-helpers' framework apply. In particular, these
commands don't work:
* `git push origin :branch-to-delete`
* `git push origin old:new` (it will push 'old') (patches available)
* `git push --dry-run origin branch` (it will push) (patches available)
****
Another limitation is that if `git log` reports a rename, this will not survive
@@ -247,9 +252,11 @@ to fully complete the conversion (prior to subsequent pushing).
Some Mercurial names (of branches, bookmarks, tags) may not be a valid git
refname. See e.g. `man git-check-ref-format` for a rather involved set of rules.
Moreover, while a slash `/` is allowed, it is not supported to have both a `parent`
and `parent/child` branch (though only the latter is allowed). So, pending some
"nice" bidirectional encoding (not even e.g. URL encoding is safe actually), it is more
likely that only a few instances (out of a whole Mercurial repo) are
and `parent/child` branch (though only the latter is allowed). Even though
it is not quite (bidirectionally) safe, a (percentage) URL encoding
(with some additional twist) is performed to obtain sane git refnames, at least
so for most cases. If some nasty cases still slip through, then likely only
a few instances (out of a whole Mercurial repo) are
problematic. This could be handled by a single-branch clone and/or configuring
a suitable refspec. However, it might be more convenient to simply filter out a
few unimportant pesky cases, which can be done by configuring a regural

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python2
#!/usr/bin/env python
#
# Copyright (c) 2016 Mark Nauwelaerts
#
@@ -16,13 +16,66 @@ import logging
import threading
# thanks go to git-remote-helper for some helper functions
# likewise so for python2/3 compatibility
def die(msg, *args):
sys.stderr.write('ERROR: %s\n' % (msg % args))
# generic
class basecompat:
@staticmethod
def char(c):
assert len(c) == 1
return c[0]
if sys.version_info[0] == 3:
import locale
class compat(basecompat):
# sigh ... wonderful python3 ... as taken from Mercurial's pycompat
@staticmethod
def decode_sysarg(arg):
if os.name == r'nt':
return arg.encode("mbcs", "ignore")
else:
enc = (
locale.getlocale()[1]
or locale.getdefaultlocale()[1]
or sys.getfilesystemencoding()
)
return arg.encode(enc, "surrogateescape")
# mostly used for straight 'cast' (not real unicode content)
@staticmethod
def to_b(s, *args):
if isinstance(s, str):
args = args or ['latin-1']
return s.encode(*args)
return s
stdin = sys.stdin.buffer
stdout = sys.stdout.buffer
stderr = sys.stderr.buffer
getcwd = os.getcwdb
getenv = os.getenvb if os.supports_bytes_environ else os.getenv
else:
class compat(basecompat):
# life was simple in those days ...
@staticmethod
def to_b(s, *args):
return s
decode_sysarg = to_b
stdin = sys.stdin
stdout = sys.stdout
stderr = sys.stderr
getcwd = staticmethod(os.getcwd)
getenv = staticmethod(os.getenv)
def puts(msg = b''):
compat.stdout.write(msg)
compat.stdout.write(b'\n')
def die(msg):
compat.stderr.write(b'ERROR: %s\n' % compat.to_b(msg, 'utf-8'))
sys.exit(1)
def warn(msg, *args):
sys.stderr.write('WARNING: %s\n' % (msg % args))
def warn(msg):
compat.stderr.write(b'WARNING: %s\n' % compat.to_b(msg, 'utf-8'))
compat.stderr.flush()
def info(msg, *args):
logger.info(msg, *args)
@@ -44,7 +97,7 @@ class GitHgRepo:
def __init__(self, topdir=None, gitdir=None):
if gitdir != None:
self.gitdir = gitdir
self.topdir = os.path.join(gitdir, '..') # will have to do
self.topdir = os.path.join(gitdir, b'..') # will have to do
else:
self.topdir = None
if not topdir:
@@ -53,7 +106,7 @@ class GitHgRepo:
if not os.path.exists('.git'):
# now we lost where we are
raise Exception('failed to determine topdir')
topdir = '.'
topdir = b'.'
self.topdir = topdir
self.gitdir = self.run_cmd(['rev-parse', '--git-dir']).strip()
if not self.gitdir:
@@ -64,7 +117,7 @@ class GitHgRepo:
self.hg_repos = {}
def identity(self):
return '[%s|%s]' % (os.getcwd(), self.topdir)
return b'[%s|%s]' % (compat.getcwd(), self.topdir or b'')
def start_cmd(self, args, **kwargs):
cmd = ['git'] + args
@@ -82,7 +135,7 @@ class GitHgRepo:
process = self.start_cmd(args, **kwargs)
output = process.communicate()[0]
if check and process.returncode != 0:
die('command failed: %s', ' '.join(cmd))
die(b'command failed: %s' % b' '.join([compat.to_b(a) for a in cmd]))
return output
def get_config(self, config, getall=False):
@@ -91,17 +144,17 @@ class GitHgRepo:
return self.run_cmd(['config', get[getall] , config], stderr=None)
def get_config_bool(self, config, default=False):
value = self.get_config(config).rstrip('\n')
if value == "true":
value = self.get_config(config).rstrip()
if value == b"true":
return True
elif value == "false":
elif value == b"false":
return False
else:
return default
def get_hg_repo_url(self, remote):
url = self.get_config('remote.%s.url' % (remote))
if url and url[0:4] == 'hg::':
url = self.get_config(b'remote.%s.url' % (remote))
if url and url[0:4] == b'hg::':
url = url[4:].strip()
else:
url = None
@@ -129,9 +182,9 @@ class GitHgRepo:
for r in self.get_hg_repos():
try:
hgpath = remotehg.select_marks_dir(r, self.gitdir, False)
m = remotehg.Marks(os.path.join(hgpath, 'marks-hg'), None)
m = remotehg.Marks(os.path.join(hgpath, b'marks-hg'), None)
mark = m.from_rev(rev)
m = GitMarks(os.path.join(hgpath, 'marks-git'))
m = GitMarks(os.path.join(hgpath, b'marks-git'))
return m.to_rev(mark)
except:
pass
@@ -143,28 +196,28 @@ class GitHgRepo:
return self.hg_repos
# check any local hg repo to see if rev is in there
shared_path = os.path.join(self.gitdir, 'hg')
hg_path = os.path.join(shared_path, '.hg')
shared_path = os.path.join(self.gitdir, b'hg')
hg_path = os.path.join(shared_path, b'.hg')
if os.path.exists(shared_path):
repos = os.listdir(shared_path)
for r in repos:
# skip the shared repo
if r == '.hg':
if r == b'.hg':
continue
# only dirs
if not os.path.isdir(os.path.join(shared_path, r)):
continue
local_path = os.path.join(shared_path, r, 'clone')
local_hg = os.path.join(local_path, '.hg')
local_path = os.path.join(shared_path, r, b'clone')
local_hg = os.path.join(local_path, b'.hg')
if not os.path.exists(local_hg):
# could be a local repo without proxy, fetch url
local_path = self.get_hg_repo_url(r)
if not local_path:
warn('failed to find local hg for remote %s', r)
warn(b'failed to find local hg for remote %s' % (r))
continue
else:
# make sure the shared path is always up-to-date
util.writefile(os.path.join(local_hg, 'sharedpath'),
util.writefile(os.path.join(local_hg, b'sharedpath'),
os.path.abspath(hg_path))
self.hg_repos[r] = os.path.join(local_path)
@@ -176,9 +229,9 @@ class GitHgRepo:
repos = self.get_hg_repos()
if r in repos:
local_path = repos[r]
hushui = ui.ui()
hushui.setconfig('ui', 'interactive', 'off')
hushui.fout = open(os.devnull, 'w')
hushui = ui.ui.load() if hasattr(ui.ui, 'load') else ui.ui()
hushui.setconfig(b'ui', b'interactive', b'off')
hushui.fout = open(os.devnull, 'wb')
return hg.repository(hushui, local_path)
def find_hg_repo(self, rev):
@@ -201,7 +254,7 @@ class GitHgRepo:
def __init__(self, repo, files):
p1, p2, data = repo[None], '0' * 40, ''
context.memctx.__init__(self, repo, (p1, p2),
data, files.keys(), self.getfilectx)
data, list(files.keys()), self.getfilectx)
self.files = files
self.remotehg = import_sibling('remotehg', 'git-remote-hg')
self.remotehg.hg_version = hg_version
@@ -215,13 +268,13 @@ class GitHgRepo:
is_link, is_exec, rename)
def read(self, relpath, rev=None):
rev = rev if rev else ':0'
obj = '%s:%s' % (rev, relpath)
rev = rev if rev else b':0'
obj = b'%s:%s' % (rev, relpath)
# might complain bitterly to stderr if no subrepos so let's not show that
return self.run_cmd(['show', obj])
# see also subrepo.state
def state(self, remote='origin', rev=None):
def state(self, remote=b'origin', rev=None):
"""return a state dict, mapping subrepo paths configured in .hgsub
to tuple: (source from .hgsub, revision from .hgsubstate, kind
(key in types dict))
@@ -229,7 +282,7 @@ class GitHgRepo:
# obtain relevant files' content from specified revision
files = { }
for f in ('.hgsub', '.hgsubstate'):
for f in (b'.hgsub', b'.hgsubstate'):
files[f] = self.read(f)
log('state files for %s in revision %s:\n%s', remote, rev, files)
@@ -237,7 +290,7 @@ class GitHgRepo:
# (rather than duplicating the admittedly simple parsing here)
repo = self.get_hg_repo(remote)
if not repo:
die('no hg repo for alias %s' % remote)
die(b'no hg repo for alias %s' % remote)
ctx = self.dictmemctx(repo, files)
# helpers moved around 4.6
if hasattr(subrepo, 'state'):
@@ -252,21 +305,21 @@ class GitHgRepo:
resolved = {}
for s in state:
src, rev, kind = state[s]
if not kind in ('hg', 'git'):
if not kind in (b'hg', b'git'):
warn('skipping unsupported subrepo type %s' % kind)
continue
if not util.url(src).isabs():
parent = self.get_hg_repo_url(remote)
if not parent:
die('could not determine repo url of %s' % remote)
die(b'could not determine repo url of %s' % remote)
parent = util.url(parent)
parent.path = posixpath.join(parent.path or '', src)
parent.path = posixpath.join(parent.path or b'', src)
parent.path = posixpath.normpath(parent.path)
src = str(parent)
src = bytes(parent)
# translate to git view url
if kind == 'hg':
src = 'hg::' + src
resolved[s] = (src.strip(), rev or '', kind)
if kind == b'hg':
src = b'hg::' + src
resolved[s] = (src.strip(), rev or b'', kind)
log('resolved state %s', resolved)
return resolved
@@ -277,12 +330,15 @@ class SubCommand:
self.subcommand = subcmdname
self.githgrepo = githgrepo
self.argparser = self.argumentparser()
# list of str
self.args = []
def argumentparser(self):
return argparse.ArgumentParser()
def get_remote(self, args):
if len(args):
assert isinstance(args[0], bytes)
return (args[0], args[1:])
else:
self.usage('missing argument: <remote-alias>')
@@ -295,7 +351,7 @@ class SubCommand:
def execute(self, args):
(self.options, self.args) = self.argparser.parse_known_args(args)
self.do(self.options, self.args)
self.do(self.options, [compat.decode_sysarg(a) for a in self.args])
def usage(self, msg):
if msg:
@@ -304,6 +360,7 @@ class SubCommand:
self.argparser.print_usage(sys.stderr)
sys.exit(2)
# args: list of bytes
def do(self, options, args):
pass
@@ -322,7 +379,7 @@ class HgRevCommand(SubCommand):
if len(args):
hgrev = self.githgrepo.get_hg_rev(args[0])
if hgrev:
print hgrev
puts(hgrev)
class GitMarks:
@@ -340,24 +397,24 @@ class GitMarks:
if not os.path.exists(self.path):
return
for l in file(self.path):
m, c = l.strip().split(' ', 2)
for l in open(self.path, 'rb'):
m, c = l.strip().split(b' ', 2)
m = int(m[1:])
self.marks[c] = m
self.rev_marks[m] = c
def store(self):
marks = self.rev_marks.keys()
marks = list(self.rev_marks.keys())
marks.sort()
with open(self.path, 'w') as f:
with open(self.path, 'wb') as f:
for m in marks:
f.write(':%d %s\n' % (m, self.rev_marks[m]))
f.write(b':%d %s\n' % (m, self.rev_marks[m]))
def from_rev(self, rev):
return self.marks[rev]
def to_rev(self, mark):
return str(self.rev_marks[mark])
return self.rev_marks[mark]
class GitRevCommand(SubCommand):
@@ -375,7 +432,7 @@ class GitRevCommand(SubCommand):
rev = args[0]
gitcommit = self.githgrepo.get_git_commit(rev)
if gitcommit:
print gitcommit
puts(gitcommit)
class GcCommand(SubCommand):
@@ -408,7 +465,7 @@ class GcCommand(SubCommand):
def print_commits(self, gm, dest):
for c in gm.marks.keys():
dest.write(c + '\n')
dest.write(c + b'\n')
dest.flush()
dest.close()
@@ -422,27 +479,27 @@ class GcCommand(SubCommand):
if not remote in hg_repos:
self.usage('%s is not a valid hg remote' % (remote))
hgpath = remotehg.select_marks_dir(remote, self.githgrepo.gitdir, False)
print "Loading hg marks ..."
hgm = remotehg.Marks(os.path.join(hgpath, 'marks-hg'), None)
print "Loading git marks ..."
gm = GitMarks(os.path.join(hgpath, 'marks-git'))
puts(b"Loading hg marks ...")
hgm = remotehg.Marks(os.path.join(hgpath, b'marks-hg'), None)
puts(b"Loading git marks ...")
gm = GitMarks(os.path.join(hgpath, b'marks-git'))
repo = hg.repository(ui.ui(), hg_repos[remote]) if options.check_hg else None
# git-gc may have dropped unreachable commits
# (in particular due to multiple hg head cases)
# need to drop those so git-fast-export or git-fast-import does not complain
print "Performing garbage collection on git commits ..."
puts(b"Performing garbage collection on git commits ...")
process = self.githgrepo.start_cmd(['cat-file', '--batch-check'], \
stdin=subprocess.PIPE)
thread = threading.Thread(target=self.print_commits, args=(gm, process.stdin))
thread.start()
git_marks = set({})
for l in process.stdout:
sp = l.strip().split(' ', 2)
sp = l.strip().split(b' ', 2)
if sp[1] == 'commit':
git_marks.add(gm.from_rev(sp[0]))
thread.join()
# reduce down to marks that are common to both
print "Computing marks intersection ..."
puts(b"Computing marks intersection ...")
common_marks = set(hgm.rev_marks.keys()).intersection(git_marks)
hg_rev_marks = {}
git_rev_marks = {}
@@ -453,7 +510,7 @@ class GcCommand(SubCommand):
git_rev_marks[m] = gm.rev_marks[m]
# common marks will not not include any refs/notes/hg
# let's not discard those casually, though they are not vital
print "Including notes commits ..."
puts(b"Including notes commits ...")
revlist = self.githgrepo.start_cmd(['rev-list', 'refs/notes/hg'])
for l in revlist.stdout.readlines():
c = l.strip()
@@ -465,24 +522,24 @@ class GcCommand(SubCommand):
git_rev_marks[hgm.last_note] = gm.rev_marks[hgm.last_note]
# some status report
if len(hgm.rev_marks) != len(hg_rev_marks):
print "Trimmed hg marks from #%d down to #%d" % (len(hgm.rev_marks), len(hg_rev_marks))
puts(b"Trimmed hg marks from #%d down to #%d" % (len(hgm.rev_marks), len(hg_rev_marks)))
if len(gm.rev_marks) != len(git_rev_marks):
print "Trimmed git marks from #%d down to #%d" % (len(gm.rev_marks), len(git_rev_marks))
puts(b"Trimmed git marks from #%d down to #%d" % (len(gm.rev_marks), len(git_rev_marks)))
# marks-hg tips irrelevant nowadays
# now update and store
if not options.dry_run:
# hg marks
print "Writing hg marks ..."
puts(b"Writing hg marks ...")
hgm.rev_marks = hg_rev_marks
hgm.marks = {}
for mark, rev in hg_rev_marks.iteritems():
for mark, rev in hg_rev_marks.items():
hgm.marks[rev] = mark
hgm.store()
# git marks
print "Writing git marks ..."
puts(b"Writing git marks ...")
gm.rev_marks = git_rev_marks
gm.marks = {}
for mark, rev in git_rev_marks.iteritems():
for mark, rev in git_rev_marks.items():
gm.marks[rev] = mark
gm.store()
@@ -491,9 +548,9 @@ class SubRepoCommand(SubCommand):
def writestate(repo, state):
"""rewrite .hgsubstate in (outer) repo with these subrepo states"""
lines = ['%s %s\n' % (state[s][1], s) for s in sorted(state)
lines = [b'%s %s\n' % (state[s][1], s) for s in sorted(state)
if state[s][1] != nullstate[1]]
repo.wwrite('.hgsubstate', ''.join(lines), '')
repo.wwrite(b'.hgsubstate', b''.join(lines), b'')
def argumentparser(self):
#usage = '%%(prog)s %s [options] <remote>...' % (self.subcommand)
@@ -630,7 +687,8 @@ class SubRepoCommand(SubCommand):
if args:
# all arguments are registered, so should not have leftover
# could be that main arguments were given to subcommands
warn('unparsed arguments: %s' % ' '.join(args))
warn(b'unparsed arguments: %s' % b' '.join(args))
options.remote = compat.decode_sysarg(options.remote)
log('running subcmd options %s, args %s', options, args)
# establish initial operation ctx
ctx = self.subcontext(self.githgrepo)
@@ -640,10 +698,10 @@ class SubRepoCommand(SubCommand):
log('running %s with options %s in context %s', \
options.func, options, ctx)
subrepos = ctx.repo.state(options.remote)
paths = subrepos.keys()
paths = list(subrepos.keys())
selabspaths = None
if ctx.level == 0 and hasattr(options, 'paths') and options.paths:
selabspaths = [ os.path.abspath(p) for p in options.paths ]
selabspaths = [ os.path.abspath(compat.decode_sysarg(p)) for p in options.paths ]
log('level %s selected paths %s', ctx.level, selabspaths)
for p in paths:
# prep context
@@ -662,17 +720,17 @@ class SubRepoCommand(SubCommand):
if not ctx.subrepo:
ctx.subrepo = self.git_hg_repo_try(ctx.subpath)
# prep recursion (only into git-hg subrepos)
if ctx.subrepo and options.recursive and ctx.state[2] == 'hg':
if ctx.subrepo and options.recursive and ctx.state[2] == b'hg':
newctx = self.subcontext(ctx.subrepo)
newctx.level = ctx.level + 1
self.do_operation(options, args, newctx)
def get_git_commit(self, ctx):
src, rev, kind = ctx.state
if kind == 'hg':
if kind == b'hg':
gitcommit = ctx.subrepo.get_git_commit(rev)
if not gitcommit:
die('could not determine git commit for %s; a fetch may update notes' % rev)
die(b'could not determine git commit for %s; a fetch may update notes' % rev)
else:
gitcommit = rev
return gitcommit
@@ -681,28 +739,28 @@ class SubRepoCommand(SubCommand):
if not ctx.subrepo:
return
src, orig, kind = ctx.state
gitcommit = ctx.subrepo.rev_parse('HEAD')
gitcommit = ctx.subrepo.rev_parse(b'HEAD')
if not gitcommit:
die('could not determine current HEAD state in %s' % ctx.subrepo.topdir)
die(b'could not determine current HEAD state in %s' % ctx.subrepo.topdir)
rev = gitcommit
if kind == 'hg':
if kind == b'hg':
rev = ctx.subrepo.get_hg_rev(gitcommit)
if not rev:
die('could not determine hg changeset for commit %s' % gitcommit)
die(b'could not determine hg changeset for commit %s' % gitcommit)
else:
rev = gitcommit
# obtain state from index
state_path = os.path.join(ctx.repo.topdir, '.hgsubstate')
state_path = os.path.join(ctx.repo.topdir, b'.hgsubstate')
# should have this, since we have subrepo (state) in the first place ...
if not os.path.exists(state_path):
die('no .hgsubstate found in repo %s' % ctx.repo.topdir)
die(b'no .hgsubstate found in repo %s' % ctx.repo.topdir)
if orig != rev:
short = ctx.subrepo.rev_parse(['--short', gitcommit])
print "Updating %s to %s [git %s]" % (ctx.subpath, rev, short)
puts(b"Updating %s to %s [git %s]" % (ctx.subpath, rev, short))
# replace and update index
with open(state_path, 'r') as f:
with open(state_path, 'rb') as f:
state = f.read()
state = re.sub('.{40} %s' % (ctx.relpath), '%s %s' % (rev, ctx.relpath), state)
state = re.sub(b'.{40} %s' % (ctx.relpath), b'%s %s' % (rev, ctx.relpath), state)
with open(state_path, 'wb') as f:
f.write(state)
@@ -710,7 +768,7 @@ class SubRepoCommand(SubCommand):
if not ctx.subrepo:
return
if not options.quiet:
print 'Entering %s' % ctx.subpath
puts(b'Entering %s' % ctx.subpath)
sys.stdout.flush()
newenv = os.environ.copy()
newenv['path'] = ctx.relpath
@@ -721,7 +779,7 @@ class SubRepoCommand(SubCommand):
proc = subprocess.Popen(options.command, shell=True, cwd=ctx.subpath, env=newenv)
proc.wait()
if proc.returncode != 0:
die('stopping at %s; script returned non-zero status' % ctx.subpath)
die(b'stopping at %s; script returned non-zero status' % ctx.subpath)
def cmd_update(self, options, args, ctx):
if not ctx.subrepo:
@@ -729,7 +787,7 @@ class SubRepoCommand(SubCommand):
self.run_cmd(options, ctx.repo, ['clone', src, ctx.subpath], cwd=None)
ctx.subrepo = self.git_hg_repo_try(ctx.subpath)
if not ctx.subrepo:
die('subrepo %s setup clone failed', ctx.subpath)
die(b'subrepo %s setup clone failed' % ctx.subpath)
# force (detached) checkout of target commit following clone
cmd = [ 'checkout', '-q' ]
else:
@@ -757,32 +815,32 @@ class SubRepoCommand(SubCommand):
def cmd_status(self, options, args, ctx):
if not ctx.subrepo:
state = '-'
revname = ''
state = b'-'
revname = b''
_, gitcommit, kind = ctx.state
if kind != 'git':
gitcommit += '[hg] '
if kind != b'git':
gitcommit += b'[hg] '
else:
gitcommit = self.get_git_commit(ctx)
head = ctx.subrepo.rev_parse('HEAD')
head = ctx.subrepo.rev_parse(b'HEAD')
if head == gitcommit:
state = ' '
state = b' '
else:
state = '+'
state = b'+'
# option determines what to print
if not options.cached:
gitcommit = head
revname = ctx.subrepo.rev_describe(gitcommit)
if revname:
revname = ' (%s)' % revname
print "%s%s %s%s" % (state, gitcommit, ctx.subpath, revname)
revname = b' (%s)' % revname
puts(b"%s%s %s%s" % (state, gitcommit, ctx.subpath, revname))
def cmd_sync(self, options, args, ctx):
if not ctx.subrepo:
return
src, _, _ = ctx.state
self.run_cmd(options, ctx.subrepo, \
['config', 'remote.%s.url' % (options.remote), src])
['config', b'remote.%s.url' % (options.remote), src])
class RepoCommand(SubCommand):
@@ -801,7 +859,7 @@ class RepoCommand(SubCommand):
(remote, args) = self.get_remote(args)
repos = self.githgrepo.get_hg_repos()
if remote in repos:
print repos[remote].rstrip('/')
puts(repos[remote].rstrip(b'/'))
class HgCommand(SubCommand):
@@ -821,8 +879,8 @@ class HgCommand(SubCommand):
remote = self.subcommand
repos = self.githgrepo.get_hg_repos()
if len(args) and remote in repos:
if args[0].find('hg') < 0:
args.insert(0, 'hg')
if args[0].find(b'hg') < 0:
args.insert(0, b'hg')
args[1:1] = ['-R', repos[remote]]
p = subprocess.Popen(args, stdout=None)
p.wait()
@@ -847,12 +905,12 @@ class HelpCommand(SubCommand):
def get_subcommands():
commands = {
'hg-rev': HgRevCommand,
'git-rev': GitRevCommand,
'repo': RepoCommand,
'gc': GcCommand,
'sub': SubRepoCommand,
'help' : HelpCommand
b'hg-rev': HgRevCommand,
b'git-rev': GitRevCommand,
b'repo': RepoCommand,
b'gc': GcCommand,
b'sub': SubRepoCommand,
b'help' : HelpCommand
}
# add remote named subcommands
repos = githgrepo.get_hg_repos()
@@ -885,11 +943,12 @@ def do_usage():
Available hg remotes:
""")
usage = compat.to_b(usage)
for r in githgrepo.get_hg_repos():
usage += '\t%s\n' % (r)
usage += '\n'
sys.stderr.write(usage)
sys.stderr.flush()
usage += b'\t%s\n' % (r)
usage += b'\n'
compat.stderr.write(usage)
compat.stderr.flush()
sys.exit(2)
def init_git(gitdir=None):
@@ -897,7 +956,7 @@ def init_git(gitdir=None):
try:
githgrepo = GitHgRepo(gitdir=gitdir)
except Exception, e:
except Exception as e:
die(str(e))
def init_logger():
@@ -916,8 +975,8 @@ def init_version():
global hg_version
try:
version, _, extra = util.version().partition('+')
version = list(int(e) for e in version.split('.'))
version, _, extra = util.version().partition(b'+')
version = list(int(e) for e in version.split(b'.'))
if extra:
version[-1] += 1
hg_version = tuple(version)
@@ -933,19 +992,21 @@ def main(argv):
global subcommands
# as an alias, cwd is top dir, change again to original directory
reldir = os.environ.get('GIT_PREFIX')
reldir = compat.getenv(b'GIT_PREFIX', None)
if reldir:
os.chdir(reldir)
# init repo dir
# we will take over dir management ...
init_git(os.environ.pop('GIT_DIR', None))
gitdir = compat.getenv(b'GIT_DIR', None)
os.environ.pop('GIT_DIR', None)
init_git(gitdir)
subcommands = get_subcommands()
cmd = ''
if len(argv) > 1:
cmd = argv[1]
cmd = compat.decode_sysarg(argv[1])
argv = argv[2:]
if cmd in subcommands:
c = subcommands[cmd]

File diff suppressed because it is too large Load Diff

45
setup.py Normal file
View File

@@ -0,0 +1,45 @@
# git-remote-hg setuptools script
import setuptools
# strip leading v
version = 'v1.0.3.1'[1:]
# check for released version
assert (len(version) > 0)
assert (version.find('-') < 0)
long_description = \
"""
'git-remote-hg' is a gitremote protocol helper for Mercurial.
It allows you to clone, fetch and push to and from Mercurial repositories as if
they were Git ones using a hg::some-url URL.
See the homepage for much more explanation.
"""
CLASSIFIERS = [
"Programming Language :: Python",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.6",
"License :: OSI Approved",
"License :: OSI Approved :: GNU General Public License v2 (GPLv2)",
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
]
setuptools.setup(name="git-remote-hg",
version=version,
author="Mark Nauwelaerts",
author_email="mnauw@users.sourceforge.net",
url="http://github.com/mnauw/git-remote-hg",
description="access hg repositories as git remotes",
long_description=long_description,
license="GPLv2",
keywords="git hg mercurial",
scripts=["git-remote-hg", "git-hg-helper"],
classifiers=CLASSIFIERS
)

View File

@@ -13,13 +13,7 @@ test -n "$TEST_DIRECTORY" || TEST_DIRECTORY=$(dirname $0)/
if ! test_have_prereq PYTHON
then
skip_all='skipping remote-hg tests; python not available'
test_done
fi
if ! python2 -c 'import mercurial' > /dev/null 2>&1
then
skip_all='skipping remote-hg tests; mercurial not available'
skip_all='skipping remote-hg tests; python with mercurial not available'
test_done
fi
@@ -243,4 +237,42 @@ test_expect_success 'hg tags' '
test_cmp expected actual
'
test_expect_success 'test timezones' '
test_when_finished "rm -rf gitrepo* hgrepo*" &&
(
git init -q gitrepo &&
cd gitrepo &&
echo alpha > alpha &&
git add alpha &&
git commit -m "add alpha" --date="2007-01-01 00:00:00 +0000" &&
echo beta > beta &&
git add beta &&
git commit -m "add beta" --date="2007-01-01 00:00:00 +0100" &&
echo gamma > gamma &&
git add gamma &&
git commit -m "add gamma" --date="2007-01-01 00:00:00 -0100" &&
echo delta > delta &&
git add delta &&
git commit -m "add delta" --date="2007-01-01 00:00:00 +0130" &&
echo epsilon > epsilon &&
git add epsilon &&
git commit -m "add epsilon" --date="2007-01-01 00:00:00 -0130"
) &&
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_done

View File

@@ -13,13 +13,7 @@ test -n "$TEST_DIRECTORY" || TEST_DIRECTORY=$(dirname $0)/
if ! test_have_prereq PYTHON
then
skip_all='skipping remote-hg tests; python not available'
test_done
fi
if ! python2 -c 'import mercurial' > /dev/null 2>&1
then
skip_all='skipping remote-hg tests; mercurial not available'
skip_all='skipping remote-hg tests; python with mercurial not available'
test_done
fi
@@ -521,8 +515,8 @@ test_expect_success 'subcommand sub status' '
(
cd gitrepo &&
git-hg-helper sub update sub_hg_a --force &&
git-hg-helper sub update sub_git --force &&
git-hg-helper sub update --force sub_hg_a &&
git-hg-helper sub update --force sub_git &&
(
# advance and add a tag to the git repo
cd sub_git &&
@@ -544,4 +538,4 @@ test_expect_success 'subcommand sub status' '
)
'
test_done
test_done

View File

@@ -13,20 +13,14 @@ test -n "$TEST_DIRECTORY" || TEST_DIRECTORY=$(dirname $0)/
if ! test_have_prereq PYTHON
then
skip_all='skipping remote-hg tests; python not available'
skip_all='skipping remote-hg tests; python with mercurial not available'
test_done
fi
if ! python2 -c 'import mercurial' > /dev/null 2>&1
then
skip_all='skipping remote-hg tests; mercurial not available'
test_done
fi
if python2 -c 'import hggit' > /dev/null 2>&1
if "$PYTHON" -c 'import hggit' > /dev/null 2>&1
then
hggit=hggit
elif python2 -c 'import hgext.git' > /dev/null 2>&1
elif "$PYTHON" -c 'import hgext.git' > /dev/null 2>&1
then
hggit=hgext.git
else
@@ -34,15 +28,6 @@ else
test_done
fi
hg_version=$(python2 -c 'from mercurial import util; print util.version()')
case $hg_version in
3.0*+*)
skip_all='skipping remote-hg tests; unsuported version of hg by hg-git'
test_done
;;
esac
# clone to a git repo with git
git_clone_git () {
git clone -q "hg::$1" $2 &&
@@ -117,10 +102,13 @@ setup () {
[extensions]
$hggit =
graphlog =
[git]
debugextrainmessage = 1
EOF
git config --global receive.denycurrentbranch warn
git config --global remote-hg.hg-git-compat true
git config --global remote-hg.track-branches false
git config --global remote-hg.shared-marks false
HGEDITOR=true
HGMERGE=true
@@ -132,6 +120,31 @@ setup () {
setup
test_expect_success 'rename' '
test_when_finished "rm -rf gitrepo* hgrepo*" &&
(
hg init hgrepo1 &&
cd hgrepo1 &&
echo alpha > alpha &&
hg add alpha &&
hg commit -m "add alpha" &&
hg mv alpha beta &&
hg commit -m "rename alpha to beta"
) &&
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 'executable bit' '
test_when_finished "rm -rf gitrepo* hgrepo*" &&

View File

@@ -270,10 +270,10 @@ test_expect_success 'push with renamed executable preserves executable bit' '
) &&
(
umask 0 &&
cd hgrepo &&
hg update &&
stat content2 >expected &&
# umask mileage might vary
grep -- -r.xr.xr.x expected
)
'

View File

@@ -22,13 +22,7 @@ fi
if ! test_have_prereq PYTHON
then
skip_all='skipping remote-hg tests; python not available'
test_done
fi
if ! python2 -c 'import mercurial' > /dev/null 2>&1
then
skip_all='skipping remote-hg tests; mercurial not available'
skip_all='skipping remote-hg tests; python with mercurial not available'
test_done
fi
@@ -96,14 +90,14 @@ check_push () {
'')
grep "^ [a-f0-9]*\.\.[a-f0-9]* *${branch} -> ${branch}$" error || ref_ret=1
;;
*)
echo "BUG: wrong kind '$kind'" && return 3
;;
esac
test $ref_ret -ne 0 && echo "match for '$branch' failed" && break
test $ref_ret -ne 0 && echo "match for '$branch' failed" && return 2
done
if test $expected_ret -ne $ret || test $ref_ret -ne 0
then
return 1
fi
test $expected_ret -ne $ret && return 1
return 0
}
@@ -686,11 +680,11 @@ test_expect_success 'remote big push' '
check_branch hgrepo default one &&
check_branch hgrepo good_branch "good branch" &&
check_branch hgrepo bad_branch "bad branch" &&
check_branch hgrepo new_branch '' &&
check_branch hgrepo new_branch &&
check_bookmark hgrepo good_bmark one &&
check_bookmark hgrepo bad_bmark1 one &&
check_bookmark hgrepo bad_bmark2 one &&
check_bookmark hgrepo new_bmark ''
check_bookmark hgrepo new_bmark
fi
'
@@ -755,7 +749,6 @@ test_expect_success 'remote big push non fast forward' '
# so it will already have pushed some above previously
# (and master is a fake one that jumps around a bit)
check_push 1 --all <<-\EOF
master:non-fast-forward
bad_bmark:non-fast-forward
branches/bad_branch:non-fast-forward
EOF
@@ -770,7 +763,7 @@ test_expect_success 'remote big push non fast forward' '
)
'
test_expect_failure 'remote big push force' '
test_expect_success 'remote big push force' '
test_when_finished "rm -rf hgrepo gitrepo*" &&
setup_big_push
@@ -778,19 +771,32 @@ test_expect_failure 'remote big push force' '
(
cd gitrepo &&
check_push 0 --force --all <<-\EOF
master
good_bmark
branches/good_branch
new_bmark:new
branches/new_branch:new
bad_bmark1:forced-update
bad_bmark2:forced-update
branches/bad_branch:forced-update
EOF
if test "$CAPABILITY_PUSH" = "t"
then
check_push 0 --force --all <<-\EOF
master:forced-update
good_bmark:forced-update
branches/good_branch:forced-update
new_bmark:new
branches/new_branch:new
bad_bmark1:forced-update
bad_bmark2:forced-update
branches/bad_branch:forced-update
EOF
else
check_push 0 --force --all <<-\EOF
master
good_bmark
branches/good_branch
new_bmark:new
branches/new_branch:new
bad_bmark1:forced-update
bad_bmark2:forced-update
branches/bad_branch:forced-update
EOF
fi
) &&
check_branch hgrepo default six &&
check_branch hgrepo good_branch eight &&
check_branch hgrepo bad_branch nine &&
check_branch hgrepo new_branch ten &&
@@ -831,11 +837,55 @@ test_expect_success 'remote big push dry-run' '
check_branch hgrepo default one &&
check_branch hgrepo good_branch "good branch" &&
check_branch hgrepo bad_branch "bad branch" &&
check_branch hgrepo new_branch '' &&
check_branch hgrepo new_branch &&
check_bookmark hgrepo good_bmark one &&
check_bookmark hgrepo bad_bmark1 one &&
check_bookmark hgrepo bad_bmark2 one &&
check_bookmark hgrepo new_bmark ''
check_bookmark hgrepo new_bmark
'
test_expect_success 'remote big push force dry-run' '
test_when_finished "rm -rf hgrepo gitrepo*" &&
setup_big_push
(
cd gitrepo &&
if test "$CAPABILITY_PUSH" = "t"
then
check_push 0 --force --dry-run --all <<-\EOF
master:forced-update
good_bmark:forced-update
branches/good_branch:forced-update
new_bmark:new
branches/new_branch:new
bad_bmark1:forced-update
bad_bmark2:forced-update
branches/bad_branch:forced-update
EOF
else
check_push 0 --force --dry-run --all <<-\EOF
master
good_bmark
branches/good_branch
new_bmark:new
branches/new_branch:new
bad_bmark1:forced-update
bad_bmark2:forced-update
branches/bad_branch:forced-update
EOF
fi
) &&
check_branch hgrepo default one &&
check_branch hgrepo good_branch "good branch" &&
check_branch hgrepo bad_branch "bad branch" &&
check_branch hgrepo new_branch &&
check_bookmark hgrepo good_bmark one &&
check_bookmark hgrepo bad_bmark1 one &&
check_bookmark hgrepo bad_bmark2 one &&
check_bookmark hgrepo new_bmark
'
test_expect_success 'remote double failed push' '
@@ -995,7 +1045,7 @@ testpushupdatesnotes='
(
cd gitrepo &&
echo two > content &&
git commit -a -m two
git commit -a -m two &&
git push
) &&
@@ -1081,7 +1131,7 @@ test_expect_success 'push merged named branch' '
git push
) &&
cat > expected <<-EOF
cat > expected <<-EOF &&
Merge
three
two
@@ -1122,7 +1172,7 @@ test_expect_success 'push tag different branch' '
cd hgrepo &&
echo one > content &&
hg add content &&
hg commit -m one
hg commit -m one &&
hg branch feature &&
echo two > content &&
hg commit -m two
@@ -1229,6 +1279,55 @@ test_expect_success 'clone can ignore invalid refnames' '
check_files gitrepo "test.txt"
'
test_expect_success 'push annotated tag' '
test_when_finished "rm -rf hgrepo gitrepo" &&
(
hg init hgrepo &&
cd hgrepo &&
echo one > content &&
hg add content &&
hg commit -m one
) &&
(
git clone "hg::hgrepo" gitrepo &&
cd gitrepo &&
git tag -m "Version 1.0" v1.0 &&
git push --tags
) &&
cat > expected <<-\EOF &&
tip:Version 1.0:C O Mitter <committer@example.com>
v1.0:one:H G Wells <wells@example.com>
EOF
hg -R hgrepo log --template "{tags}:{desc}:{author}\n" > actual &&
test_cmp expected actual
'
test_expect_success 'timezone issues with negative offsets' '
test_when_finished "rm -rf hgrepo gitrepo1 gitrepo2" &&
hg init hgrepo &&
(
git clone "hg::hgrepo" gitrepo1 &&
cd gitrepo1 &&
echo two >> content &&
git add content &&
git commit -m two --date="2016-09-26 00:00:00 -0230" &&
git push
) &&
git clone "hg::hgrepo" gitrepo2 &&
git --git-dir=gitrepo1/.git log -1 --format="%ai" > expected &&
git --git-dir=gitrepo2/.git log -1 --format="%ai" > actual &&
test_cmp expected actual
'
if test "$CAPABILITY_PUSH" != "t"
then
test_done

View File

@@ -18,33 +18,80 @@
# along with this program. If not, see http://www.gnu.org/licenses/ .
# Public: Current version of Sharness.
SHARNESS_VERSION="0.3.0"
SHARNESS_VERSION="1.1.0"
export SHARNESS_VERSION
# Public: The file extension for tests. By default, it is set to "t".
: ${SHARNESS_TEST_EXTENSION:=t}
: "${SHARNESS_TEST_EXTENSION:=t}"
export SHARNESS_TEST_EXTENSION
# Keep the original TERM for say_color
ORIGINAL_TERM=$TERM
# Public: Root directory containing tests. Tests can override this variable,
# e.g. for testing Sharness itself.
if test -z "$SHARNESS_TEST_DIRECTORY"
then
SHARNESS_TEST_DIRECTORY=$(pwd)
else
# ensure that SHARNESS_TEST_DIRECTORY is an absolute path so that it
# is valid even if the current working directory is changed
SHARNESS_TEST_DIRECTORY=$(cd "$SHARNESS_TEST_DIRECTORY" && pwd) || exit 1
fi
export SHARNESS_TEST_DIRECTORY
if test -z "$SHARNESS_TEST_OUTPUT_DIRECTORY"
then
# Similarly, override this to store the test-results subdir
# elsewhere
SHARNESS_TEST_OUTPUT_DIRECTORY=$SHARNESS_TEST_DIRECTORY
fi
# Reset TERM to original terminal if found, otherwise save original TERM
[ "x" = "x$SHARNESS_ORIG_TERM" ] &&
SHARNESS_ORIG_TERM="$TERM" ||
TERM="$SHARNESS_ORIG_TERM"
# Public: The unsanitized TERM under which sharness is originally run
export SHARNESS_ORIG_TERM
# Export SHELL_PATH
: "${SHELL_PATH:=$SHELL}"
export SHELL_PATH
# if --tee was passed, write the output not only to the terminal, but
# additionally to the file test-results/$BASENAME.out, too.
case "$SHARNESS_TEST_TEE_STARTED, $* " in
done,*)
# do not redirect again
;;
*' --tee '*|*' --verbose-log '*)
mkdir -p "$SHARNESS_TEST_OUTPUT_DIRECTORY/test-results"
BASE="$SHARNESS_TEST_OUTPUT_DIRECTORY/test-results/$(basename "$0" ".$SHARNESS_TEST_EXTENSION")"
# Make this filename available to the sub-process in case it is using
# --verbose-log.
SHARNESS_TEST_TEE_OUTPUT_FILE="$BASE.out"
export SHARNESS_TEST_TEE_OUTPUT_FILE
# Truncate before calling "tee -a" to get rid of the results
# from any previous runs.
: >"$SHARNESS_TEST_TEE_OUTPUT_FILE"
(SHARNESS_TEST_TEE_STARTED="done" ${SHELL_PATH} "$0" "$@" 2>&1;
echo $? >"$BASE.exit") | tee -a "$SHARNESS_TEST_TEE_OUTPUT_FILE"
test "$(cat "$BASE.exit")" = 0
exit
;;
esac
# For repeatability, reset the environment to a known state.
# TERM is sanitized below, after saving color control sequences.
LANG=C
LC_ALL=C
PAGER=cat
PAGER="cat"
TZ=UTC
TERM=dumb
EDITOR=:
export LANG LC_ALL PAGER TZ TERM EDITOR
export LANG LC_ALL PAGER TZ EDITOR
unset VISUAL CDPATH GREP_OPTIONS
# Line feed
LF='
'
[ "x$ORIGINAL_TERM" != "xdumb" ] && (
TERM=$ORIGINAL_TERM &&
export TERM &&
[ "x$TERM" != "xdumb" ] && (
[ -t 1 ] &&
tput bold >/dev/null 2>&1 &&
tput setaf 1 >/dev/null 2>&1 &&
@@ -60,6 +107,8 @@ while test "$#" -ne 0; do
immediate=t; shift ;;
-l|--l|--lo|--lon|--long|--long-|--long-t|--long-te|--long-tes|--long-test|--long-tests)
TEST_LONG=t; export TEST_LONG; shift ;;
--in|--int|--inte|--inter|--intera|--interac|--interact|--interacti|--interactiv|--interactive|--interactive-|--interactive-t|--interactive-te|--interactive-tes|--interactive-test|--interactive-tests):
TEST_INTERACTIVE=t; export TEST_INTERACTIVE; verbose=t; shift ;;
-h|--h|--he|--hel|--help)
help=t; shift ;;
-v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
@@ -68,49 +117,69 @@ while test "$#" -ne 0; do
# Ignore --quiet under a TAP::Harness. Saying how many tests
# passed without the ok/not ok details is always an error.
test -z "$HARNESS_ACTIVE" && quiet=t; shift ;;
--chain-lint)
chain_lint=t; shift ;;
--no-chain-lint)
chain_lint=; shift ;;
--no-color)
color=; shift ;;
--tee)
shift ;; # was handled already
--root=*)
root=$(expr "z$1" : 'z[^=]*=\(.*\)')
shift ;;
--verbose-log)
verbose_log=t
shift ;;
*)
echo "error: unknown test option '$1'" >&2; exit 1 ;;
esac
done
if test -n "$color"; then
# Save the color control sequences now rather than run tput
# each time say_color() is called. This is done for two
# reasons:
# * TERM will be changed to dumb
# * HOME will be changed to a temporary directory and tput
# might need to read ~/.terminfo from the original HOME
# directory to get the control sequences
# Note: This approach assumes the control sequences don't end
# in a newline for any terminal of interest (command
# substitutions strip trailing newlines). Given that most
# (all?) terminals in common use are related to ECMA-48, this
# shouldn't be a problem.
say_color_error=$(tput bold; tput setaf 1) # bold red
say_color_skip=$(tput setaf 4) # blue
say_color_warn=$(tput setaf 3) # brown/yellow
say_color_pass=$(tput setaf 2) # green
say_color_info=$(tput setaf 6) # cyan
say_color_reset=$(tput sgr0)
say_color_raw="" # no formatting for normal text
say_color() {
(
TERM=$ORIGINAL_TERM
export TERM
test -z "$1" && test -n "$quiet" && return
case "$1" in
error)
tput bold; tput setaf 1;; # bold red
skip)
tput setaf 4;; # blue
warn)
tput setaf 3;; # brown/yellow
pass)
tput setaf 2;; # green
info)
tput setaf 6;; # cyan
*)
test -n "$quiet" && return;;
error) say_color_color=$say_color_error ;;
skip) say_color_color=$say_color_skip ;;
warn) say_color_color=$say_color_warn ;;
pass) say_color_color=$say_color_pass ;;
info) say_color_color=$say_color_info ;;
*) say_color_color=$say_color_raw ;;
esac
shift
printf "%s" "$*"
tput sgr0
echo
)
printf '%s%s%s\n' "$say_color_color" "$*" "$say_color_reset"
}
else
say_color() {
test -z "$1" && test -n "$quiet" && return
shift
printf "%s\n" "$*"
printf '%s\n' "$*"
}
fi
TERM=dumb
export TERM
error() {
say_color error "error: $*"
EXIT_OK=t
@@ -121,7 +190,7 @@ say() {
say_color info "$*"
}
test -n "$test_description" || error "Test script did not set test_description."
test -n "${test_description:-}" || error "Test script did not set test_description."
if test "$help" = "t"; then
echo "$test_description"
@@ -130,7 +199,11 @@ fi
exec 5>&1
exec 6<&0
if test "$verbose" = "t"; then
if test "$verbose_log" = "t"
then
exec 3>>"$SHARNESS_TEST_TEE_OUTPUT_FILE" 4>&3
elif test "$verbose" = "t"
then
exec 4>&2 3>&1
else
exec 4>/dev/null 3>/dev/null
@@ -161,7 +234,7 @@ trap 'die' EXIT
# implicitly by specifying the prerequisite name in calls to test_expect_success
# or test_expect_failure.
#
# $1 - Name of prerequiste (a simple word, in all capital letters by convention)
# $1 - Name of prerequisite (a simple word, in all capital letters by convention)
#
# Examples
#
@@ -198,7 +271,7 @@ test_have_prereq() {
# prerequisites can be concatenated with ','
save_IFS=$IFS
IFS=,
set -- $*
set -- $@
IFS=$save_IFS
total_prereq=0
@@ -215,7 +288,7 @@ test_have_prereq() {
negative_prereq=
esac
total_prereq=$(($total_prereq + 1))
total_prereq=$((total_prereq + 1))
case "$satisfied_prereq" in
*" $prerequisite "*)
satisfied_this_prereq=t
@@ -226,7 +299,7 @@ test_have_prereq() {
case "$satisfied_this_prereq,$negative_prereq" in
t,|,t)
ok_prereq=$(($ok_prereq + 1))
ok_prereq=$((ok_prereq + 1))
;;
*)
# Keep a list of missing prerequisites; restore
@@ -247,12 +320,12 @@ test_have_prereq() {
# the text_expect_* functions instead.
test_ok_() {
test_success=$(($test_success + 1))
say_color "" "ok $test_count - $@"
test_success=$((test_success + 1))
say_color "" "ok $test_count - $*"
}
test_failure_() {
test_failure=$(($test_failure + 1))
test_failure=$((test_failure + 1))
say_color error "not ok $test_count - $1"
shift
echo "$@" | sed -e 's/^/# /'
@@ -260,13 +333,13 @@ test_failure_() {
}
test_known_broken_ok_() {
test_fixed=$(($test_fixed + 1))
say_color error "ok $test_count - $@ # TODO known breakage vanished"
test_fixed=$((test_fixed + 1))
say_color error "ok $test_count - $* # TODO known breakage vanished"
}
test_known_broken_failure_() {
test_broken=$(($test_broken + 1))
say_color warn "not ok $test_count - $@ # TODO known breakage"
test_broken=$((test_broken + 1))
say_color warn "not ok $test_count - $* # TODO known breakage"
}
# Public: Execute commands in debug mode.
@@ -287,10 +360,29 @@ test_debug() {
test "$debug" = "" || eval "$1"
}
# Public: Stop execution and start a shell.
#
# This is useful for debugging tests and only makes sense together with "-v".
# Be sure to remove all invocations of this command before submitting.
test_pause() {
if test "$verbose" = t; then
"$SHELL_PATH" <&6 >&3 2>&4
else
error >&5 "test_pause requires --verbose"
fi
}
test_eval_() {
# This is a separate function because some tests use
# "return" to end a test_expect_success block early.
eval </dev/null >&3 2>&4 "$*"
case ",$test_prereq," in
*,INTERACTIVE,*)
eval "$*"
;;
*)
eval </dev/null >&3 2>&4 "$*"
;;
esac
}
test_run_() {
@@ -299,6 +391,13 @@ test_run_() {
test_eval_ "$1"
eval_ret=$?
if test "$chain_lint" = "t"; then
test_eval_ "(exit 117) && $1"
if test "$?" != 117; then
error "bug in the test script: broken &&-chain: $1"
fi
fi
if test -z "$immediate" || test $eval_ret = 0 || test -n "$expecting_failure"; then
test_eval_ "$test_cleanup"
fi
@@ -309,7 +408,7 @@ test_run_() {
}
test_skip_() {
test_count=$(($test_count + 1))
test_count=$((test_count + 1))
to_skip=
for skp in $SKIP_TESTS; do
case $this_test.$test_count in
@@ -328,7 +427,7 @@ test_skip_() {
of_prereq=" of $test_prereq"
fi
say_color skip >&3 "skipping test: $@"
say_color skip >&3 "skipping test: $*"
say_color skip "ok $test_count # skip $1 (missing $missing_prereq${of_prereq})"
: true
;;
@@ -426,6 +525,44 @@ test_expect_failure() {
echo >&3 ""
}
# Public: Run test commands and expect anything from them. Used when a
# test is not stable or not finished for some reason.
#
# When the test passed, an "ok" message is printed, but the number of
# fixed tests is not incremented.
#
# When it failed, a "not ok ... # TODO known breakage" message is
# printed, and the number of tests still broken is incremented.
#
# Failures from these tests won't cause --immediate to stop.
#
# Usually takes two arguments:
# $1 - Test description
# $2 - Commands to be executed.
#
# With three arguments, the first will be taken to be a prerequisite:
# $1 - Comma-separated list of test prerequisites. The test will be skipped if
# not all of the given prerequisites are set. To negate a prerequisite,
# put a "!" in front of it.
# $2 - Test description
# $3 - Commands to be executed.
#
# Returns nothing.
test_expect_unstable() {
test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
test "$#" = 2 || error "bug in the test script: not 2 or 3 parameters to test_expect_unstable"
export test_prereq
if ! test_skip_ "$@"; then
say >&3 "checking unstable test: $2"
if test_run_ "$2" unstable; then
test_ok_ "$1"
else
test_known_broken_failure_ "$1"
fi
fi
echo >&3 ""
}
# Public: Run command and ensure that it fails in a controlled way.
#
# Use it instead of "! <command>". For example, when <command> dies due to a
@@ -518,7 +655,7 @@ test_expect_code() {
shift
"$@"
exit_code=$?
if test $exit_code = $want_code; then
if test "$exit_code" = "$want_code"; then
return 0
fi
@@ -528,7 +665,7 @@ test_expect_code() {
# Public: Compare two files to see if expected output matches actual output.
#
# The TEST_CMP variable defines the command used for the comparision; it
# The TEST_CMP variable defines the command used for the comparison; it
# defaults to "diff -u". Only when the test script was started with --verbose,
# will the command's output, the diff, be printed to the standard output.
#
@@ -551,6 +688,79 @@ test_cmp() {
${TEST_CMP:-diff -u} "$@"
}
# Public: portably print a sequence of numbers.
#
# seq is not in POSIX and GNU seq might not be available everywhere,
# so it is nice to have a seq implementation, even a very simple one.
#
# $1 - Starting number.
# $2 - Ending number.
#
# Examples
#
# test_expect_success 'foo works 10 times' '
# for i in $(test_seq 1 10)
# do
# foo || return
# done
# '
#
# Returns 0 if all the specified numbers can be displayed.
test_seq() {
i="$1"
j="$2"
while test "$i" -le "$j"
do
echo "$i" || return
i=$(("$i" + 1))
done
}
# Public: Check if the file expected to be empty is indeed empty, and barfs
# otherwise.
#
# $1 - File to check for emptiness.
#
# Returns 0 if file is empty, 1 otherwise.
test_must_be_empty() {
if test -s "$1"
then
echo "'$1' is not empty, it contains:"
cat "$1"
return 1
fi
}
# debugging-friendly alternatives to "test [-f|-d|-e]"
# The commands test the existence or non-existence of $1. $2 can be
# given to provide a more precise diagnosis.
test_path_is_file () {
if ! test -f "$1"
then
echo "File $1 doesn't exist. $2"
false
fi
}
test_path_is_dir () {
if ! test -d "$1"
then
echo "Directory $1 doesn't exist. $2"
false
fi
}
# Check if the directory exists and is empty as expected, barf otherwise.
test_dir_is_empty () {
test_path_is_dir "$1" &&
if test -n "$(find "$1" -mindepth 1 -maxdepth 1)"
then
echo "Directory '$1' is not empty, it contains:"
ls -la "$1"
return 1
fi
}
# Public: Schedule cleanup commands to be run unconditionally at the end of a
# test.
#
@@ -576,6 +786,23 @@ test_when_finished() {
} && (exit \"\$eval_ret\"); eval_ret=\$?; $test_cleanup"
}
# Public: Schedule cleanup commands to be run unconditionally when all tests
# have run.
#
# This can be used to clean up things like test databases. It is not needed to
# clean up temporary files, as test_done already does that.
#
# Examples:
#
# cleanup mysql -e "DROP DATABASE mytest"
#
# Returns the exit code of the last cleanup command executed.
final_cleanup=
cleanup() {
final_cleanup="{ $*
} && (exit \"\$eval_ret\"); eval_ret=\$?; $final_cleanup"
}
# Public: Summarize test results and exit with an appropriate error code.
#
# Must be called at the end of each test script.
@@ -600,9 +827,9 @@ test_done() {
EXIT_OK=t
if test -z "$HARNESS_ACTIVE"; then
test_results_dir="$SHARNESS_TEST_DIRECTORY/test-results"
test_results_dir="$SHARNESS_TEST_OUTPUT_DIRECTORY/test-results"
mkdir -p "$test_results_dir"
test_results_path="$test_results_dir/${SHARNESS_TEST_FILE%.$SHARNESS_TEST_EXTENSION}.$$.counts"
test_results_path="$test_results_dir/$this_test.$$.counts"
cat >>"$test_results_path" <<-EOF
total $test_count
@@ -621,7 +848,7 @@ test_done() {
say_color warn "# still have $test_broken known breakage(s)"
fi
if test "$test_broken" != 0 || test "$test_fixed" != 0; then
test_remaining=$(( $test_count - $test_broken - $test_fixed ))
test_remaining=$((test_count - test_broken - test_fixed))
msg="remaining $test_remaining test(s)"
else
test_remaining=$test_count
@@ -641,6 +868,8 @@ test_done() {
fi
say "1..$test_count$skip_all"
test_eval_ "$final_cleanup"
test -d "$remove_trash" &&
cd "$(dirname "$remove_trash")" &&
rm -rf "$(basename "$remove_trash")"
@@ -656,14 +885,15 @@ test_done() {
esac
}
# Public: Root directory containing tests. Tests can override this variable,
# e.g. for testing Sharness itself.
: ${SHARNESS_TEST_DIRECTORY:=$(pwd)}
export SHARNESS_TEST_DIRECTORY
# Public: Source directory of test code and sharness library.
# This directory may be different from the directory in which tests are
# being run.
: "${SHARNESS_TEST_SRCDIR:=$(cd "$(dirname "$0")" && pwd)}"
export SHARNESS_TEST_SRCDIR
# Public: Build directory that will be added to PATH. By default, it is set to
# the parent directory of SHARNESS_TEST_DIRECTORY.
: ${SHARNESS_BUILD_DIRECTORY:="$SHARNESS_TEST_DIRECTORY/.."}
: "${SHARNESS_BUILD_DIRECTORY:="$SHARNESS_TEST_DIRECTORY/.."}"
PATH="$SHARNESS_BUILD_DIRECTORY:$PATH"
export PATH SHARNESS_BUILD_DIRECTORY
@@ -672,19 +902,43 @@ SHARNESS_TEST_FILE="$0"
export SHARNESS_TEST_FILE
# Prepare test area.
test_dir="trash directory.$(basename "$SHARNESS_TEST_FILE" ".$SHARNESS_TEST_EXTENSION")"
test -n "$root" && test_dir="$root/$test_dir"
case "$test_dir" in
/*) SHARNESS_TRASH_DIRECTORY="$test_dir" ;;
*) SHARNESS_TRASH_DIRECTORY="$SHARNESS_TEST_DIRECTORY/$test_dir" ;;
SHARNESS_TRASH_DIRECTORY="trash directory.$(basename "$SHARNESS_TEST_FILE" ".$SHARNESS_TEST_EXTENSION")"
test -n "$root" && SHARNESS_TRASH_DIRECTORY="$root/$SHARNESS_TRASH_DIRECTORY"
case "$SHARNESS_TRASH_DIRECTORY" in
/*) ;; # absolute path is good
*) SHARNESS_TRASH_DIRECTORY="$SHARNESS_TEST_OUTPUT_DIRECTORY/$SHARNESS_TRASH_DIRECTORY" ;;
esac
test "$debug" = "t" || remove_trash="$SHARNESS_TRASH_DIRECTORY"
rm -rf "$test_dir" || {
rm -rf "$SHARNESS_TRASH_DIRECTORY" || {
EXIT_OK=t
echo >&5 "FATAL: Cannot prepare test area"
exit 1
}
#
# Load any extensions in $srcdir/sharness.d/*.sh
#
if test -d "${SHARNESS_TEST_SRCDIR}/sharness.d"
then
for file in "${SHARNESS_TEST_SRCDIR}"/sharness.d/*.sh
do
# Ensure glob was not an empty match:
test -e "${file}" || break
if test -n "$debug"
then
echo >&5 "sharness: loading extensions from ${file}"
fi
. "${file}"
if test $? != 0
then
echo >&5 "sharness: Error loading ${file}. Aborting."
exit 1
fi
done
fi
# Public: Empty trash directory, the test area, provided for each test. The HOME
# variable is set to that directory too.
export SHARNESS_TRASH_DIRECTORY
@@ -692,10 +946,10 @@ export SHARNESS_TRASH_DIRECTORY
HOME="$SHARNESS_TRASH_DIRECTORY"
export HOME
mkdir -p "$test_dir" || exit 1
mkdir -p "$SHARNESS_TRASH_DIRECTORY" || exit 1
# Use -P to resolve symlinks in our working directory so that the cwd
# in subprocesses like git equals our $PWD (for pathname comparisons).
cd -P "$test_dir" || exit 1
cd -P "$SHARNESS_TRASH_DIRECTORY" || exit 1
this_test=${SHARNESS_TEST_FILE##*/}
this_test=${this_test%.$SHARNESS_TEST_EXTENSION}
@@ -708,4 +962,10 @@ for skp in $SKIP_TESTS; do
esac
done
test -n "$TEST_LONG" && test_set_prereq EXPENSIVE
test -n "$TEST_INTERACTIVE" && test_set_prereq INTERACTIVE
# Make sure this script ends with code 0
:
# vi: set ts=4 sw=4 noet :

View File

@@ -1,8 +1,60 @@
#!/bin/sh
. ./sharness.sh
if [ -z "$SHARNESS" ] ; then
for d in \
"." \
"$HOME/share/sharness" \
"/usr/local/share/sharness" \
"/usr/share/sharness"
do
f="$d/sharness.sh"
if [ -f "$f" ] ; then
SHARNESS="$f"
fi
done
fi
if [ -z "$SHARNESS" ] || [ ! -f "$SHARNESS" ] ; then
echo "sharness.sh not found" >&2
exit 1
fi
test_set_prereq PYTHON
# Prevent sharness from adding the source directory to PATH
# since the scripts use unversioned python for their shebang
# but tests should run under the python with mercurial support
# so create an empty directory and strip it from PATH afterwards
SHARNESS_BUILD_DIRECTORY="$(mktemp -d)"
. "$SHARNESS"
export PATH="${PATH#*:}"
rmdir "$SHARNESS_BUILD_DIRECTORY"
if [ -z "$TEST_INSTALLED_SCRIPTS" ] ; then
if [ -n "$PYTHON" ] && "$PYTHON" -c 'import mercurial' 2> /dev/null ; then
: Use chosen Python version
elif python3 -c 'import mercurial' 2> /dev/null ; then
PYTHON=python3
elif python2 -c 'import mercurial' 2> /dev/null ; then
PYTHON=python2
elif python -c 'import mercurial' 2> /dev/null ; then
PYTHON=python
fi
if [ -n "$PYTHON" ] ; then
test_set_prereq PYTHON
# Change shebang on a copy of scripts to chosen Python version
TEST_BIN="$SHARNESS_TRASH_DIRECTORY/bin"
mkdir -p "$TEST_BIN"
for s in git-remote-hg git-hg-helper ; do
printf "%s\n" "#!/usr/bin/env $PYTHON" > "$TEST_BIN/$s"
tail -n +2 "$SHARNESS_TEST_DIRECTORY/../$s" >> "$TEST_BIN/$s"
chmod u+x "$TEST_BIN/$s"
done
export PATH="$TEST_BIN${PATH:+:$PATH}"
unset TEST_BIN
fi
else
# The build/install process ensures Python is available
test_set_prereq PYTHON
fi
GIT_AUTHOR_EMAIL=author@example.com
GIT_AUTHOR_NAME='A U Thor'
@@ -10,3 +62,6 @@ GIT_COMMITTER_EMAIL=committer@example.com
GIT_COMMITTER_NAME='C O Mitter'
export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME
export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME
# maintain backwards compatible default
# (as used in remote helper)
git config --global init.defaultBranch master

1
tools/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
results.txt

303
tools/check-versions Executable file
View File

@@ -0,0 +1,303 @@
#!/usr/bin/env ruby
#
# Copyright (c) 2019 Felipe Contreras
#
# This script runs the tests for all versions of the components:
# hg, hggit and dulwich
#
# You can run it without arguments, in which case it reads the file
# 'versions.txt' and executes all those checks.
#
# Or you can pass the versions to check manually, like:
#
# ./check-versions hg:4.7 hggit:0.8.12 dulwich:0.19.7
#
# Or you can pass just the hg version, the other versions are fetched from
# 'versions.txt':
#
# ./check-versions hg:5.0
#
require 'fileutils'
require 'tmpdir'
$tests = %w[main.t bidi.t hg-git.t]
$workdir = "#{Dir.home}/.cache/git-remote-hg"
$builddir = Dir.mktmpdir("git-remote-hg-build-")
$testoutdir = Dir.mktmpdir("git-remote-hg-tests-")
at_exit {
FileUtils.remove_entry($builddir)
FileUtils.remove_entry($testoutdir)
}
QUIET, LOW, HIGH = (1..3).to_a
$verbosity = LOW
# Util {{{1
def section(text)
puts [nil, text, '=' * text.size]
end
def title(text)
puts [nil, text, '-' * text.size] unless $verbosity < HIGH
end
def run_cmd(cmd, fatal: true)
puts cmd.join(' ') unless $verbosity < HIGH
result = system(*cmd)
unless result or not fatal
STDERR.puts "Failed to run command '%s'" % cmd.join(' ')
exit -1
end
result
end
def check_version(a, b)
return true if a == '@'
a = a.split('.').map(&:to_i)
b = b.split('.').map(&:to_i)
(a <=> b) >= 0
end
# Component {{{1
class Component
attr_reader :id
def initialize(id, url, kind: nil, **args)
@id = id
@url = url
@kind = kind || (url.start_with?('git') ? :git : :hg)
@tool = @kind.to_s
@checkout_fix = args[:checkout_fix]
@version_format = args[:version_format]
end
def dir
"#{$workdir}/#{@id}"
end
def get_version(version)
return @kind == :hg ? 'tip' : '@' if version == '@'
@version_format ? @version_format % version : version
end
def clone
run_cmd [@tool, 'clone', '-q', @url, dir]
end
def checkout(version)
Dir.chdir(dir) do
case @kind
when :hg
cmd = %w[update --clean]
when :git
cmd = %w[reset --hard]
else
cmd = %w[checkout]
end
run_cmd [@tool] + cmd + ['-q', get_version(version)]
@checkout_fix.call(version) if @checkout_fix
end
end
def build
Dir.chdir(dir) do
targets = %w[build_py build_ext].map { |e| [e, '--build-lib', "#{$builddir}/python"] }
run_cmd %w[python setup.py --quiet] + targets.flatten
end
end
end
# Functions {{{1
def setup
dirs = %w[bin python]
FileUtils.mkdir_p(dirs.map { |e| "#{$builddir}/#{e}" })
FileUtils.mkdir_p($workdir)
$components.each do |id, component|
next if File.exists?(component.dir)
if $verbosity < HIGH
puts "Cloning #{component.id}"
else
title "Cloning #{component.id}"
end
component.clone
end
end
def test_env(paths: nil)
old = ENV.to_h
paths.each do |id, path|
name = id.to_s
ENV[name] = "#{path}:#{ENV[name]}"
end
r = yield
ENV.replace(old)
return r
end
def run_tests(tests)
title "Running tests"
Dir.chdir("#{__dir__}/../test") do
case $verbosity
when QUIET
tests_opt = tests.join(' ')
cmd = "prove -q #{tests_opt} :: -i"
when LOW
tests_opt = "T='%s'" % tests.join(' ')
cmd = "make -j1 #{tests_opt}"
else
tests_opt = "T='%s'" % tests.join(' ')
cmd = "TEST_OPTS='-v -i' make -j1 #{tests_opt}"
end
system(cmd)
end
end
def versions_to_s(versions)
versions.map { |k,v| "#{k}:#{v}" }.join(' ')
end
def versions_from_args(args)
args.map { |e| k, v = e.split(':'); [k.to_sym, v] }.to_h
end
def versions_from_s(str)
versions_from_args(str.split(' '))
end
def check(versions)
section versions_to_s(versions)
versions.each do |id, version|
component = $components[id]
next unless component
title "Checking out #{component.id} #{version}"
component.checkout(version)
title "Building #{component.id}"
component.build
end
paths = {
PATH: "#{$builddir}/bin",
PYTHONPATH: "#{$builddir}/python",
}
test_env(paths: paths) do
ENV['SHARNESS_TEST_OUTPUT_DIRECTORY'] = $testoutdir
run_tests($tests)
end
end
# Add components {{{1
$components = {}
def add_component(id, url, **args)
$components[id] = Component.new(id, url, **args)
end
hg_checkout_fix = lambda do |version|
FileUtils.cp('hg', "#{$builddir}/bin/")
return if check_version(version, '4.3')
if run_cmd %W[hg import -q --no-commit #{__dir__}/hg_setup_hack_2.4.patch], fatal: false
File.write('.hg_force_version', "%s\n" % version)
else
File.write('mercurial/__version__.py', "version = \"%s\"\n" % version)
end
end
add_component(:hg, 'https://www.mercurial-scm.org/repo/hg', checkout_fix: hg_checkout_fix)
hggit_checkout_fix = lambda do |version|
return unless check_version(version, '0.8.0')
run_cmd %W[hg import -q --no-commit #{__dir__}/hggit_rename_fix_0.8.0.patch], fatal: false
end
add_component(:hggit, 'https://bitbucket.org/durin42/hg-git', checkout_fix: hggit_checkout_fix)
add_component(:dulwich, 'https://github.com/dulwich/dulwich.git', version_format: 'dulwich-%s', kind: :git)
def load_checks(file)
file.each do |e|
e.chomp!
next if e.empty? or e.start_with?('#')
content, comment = e.split(' # ')
versions = versions_from_s(content)
$checks << versions
end
end
def store_results(file)
$results.each do |versions, result|
content = versions_to_s(versions)
comment = result ? 'OK' : 'FAIL'
file.puts '%s # %s' % [content, comment]
end
end
# Main {{{1
setup
$checks = []
$results = []
$versions = versions_from_args(ARGV)
File.open("#{__dir__}/versions.txt") do |f|
load_checks(f)
end
if $versions.size == 1 and $versions.key?(:hg)
# mode 1
$verbosity = LOW
if ['@', nil].include?($versions[:hg])
versions = $checks.last
versions[:hg] = $versions[:hg] if $versions[:hg]
else
versions = $checks.find { |e| e[:hg] == $versions[:hg] }
exit 1 unless versions
end
exit check(versions) ? 0 : 1
elsif not $versions.empty?
# mode 2
$verbosity = HIGH
exit check(versions) ? 0 : 1
else
# mode 3
$verbosity = QUIET
at_exit do
File.open("#{__dir__}/results.txt", 'w') do |f|
store_results(f)
end
end
failures = 0
$checks.each do |versions|
result = check(versions)
failures += 1 unless result
$results << [versions, result]
end
exit 1 unless failures == 0
end

View File

@@ -0,0 +1,15 @@
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -181,7 +181,10 @@
# error 0xc0150004. See: http://bugs.python.org/issue3440
env['SystemRoot'] = os.environ['SystemRoot']
-if os.path.isdir('.hg'):
+if os.path.exists('.hg_force_version'):
+ with open('.hg_force_version') as f:
+ version = f.read().rstrip('\n')
+elif os.path.isdir('.hg'):
cmd = [sys.executable, 'hg', 'log', '-r', '.', '--template', '{tags}\n']
numerictags = [t for t in runhg(cmd, env).split() if t[0].isdigit()]
hgid = runhg([sys.executable, 'hg', 'id', '-i'], env).strip()

View File

@@ -0,0 +1,22 @@
diff --git a/hggit/git_handler.py b/hggit/git_handler.py
--- a/hggit/git_handler.py
+++ b/hggit/git_handler.py
@@ -693,6 +693,8 @@
def import_git_commit(self, commit):
self.ui.debug(_("importing: %s\n") % commit.id)
+ extra_in_message = self.ui.configbool('git', 'debugextrainmessage', False)
+
detect_renames = False
(strip_message, hg_renames,
hg_branch, extra) = git2hg.extract_hg_metadata(
@@ -703,7 +705,8 @@
# renames detected from Git. This is because we export an extra
# 'HG:rename-source' Git parameter when this isn't set, which will
# break bidirectionality.
- extra['hg-git-rename-source'] = 'git'
+ if not extra_in_message:
+ extra['hg-git-rename-source'] = 'git'
else:
renames = hg_renames

33
tools/versions.txt Normal file
View File

@@ -0,0 +1,33 @@
# vi: ft=ruby
hg:2.4 hggit:0.4.0 dulwich:0.9.0 # 2013_02
hg:2.5 hggit:0.4.0 dulwich:0.9.0 # 2013_02
hg:2.6 hggit:0.4.0 dulwich:0.9.0 # 2013_02
hg:2.7 hggit:0.4.0 dulwich:0.9.0 # 2013_02
hg:2.8 hggit:0.4.0 dulwich:0.9.0 # 2013_02
hg:2.9 hggit:0.4.0 dulwich:0.9.0 # 2013_02
hg:3.0 hggit:0.7.0 dulwich:0.10.0 # 2014_11
hg:3.1 hggit:0.7.0 dulwich:0.10.0 # 2014_11
hg:3.2 hggit:0.7.0 dulwich:0.10.0 # 2014_11
hg:3.3 hggit:0.8.4 dulwich:0.13.0 # 2016_01
hg:3.4 hggit:0.8.4 dulwich:0.13.0 # 2016_01
hg:3.5 hggit:0.8.4 dulwich:0.13.0 # 2016_01
hg:3.6 hggit:0.8.4 dulwich:0.13.0 # 2016_01
hg:3.7 hggit:0.8.4 dulwich:0.13.0 # 2016_01
hg:3.8 hggit:0.8.4 dulwich:0.13.0 # 2016_01
hg:3.9 hggit:0.8.4 dulwich:0.13.0 # 2016_01
hg:4.0 hggit:0.8.10 dulwich:0.18.0 # 2017_11
hg:4.1 hggit:0.8.10 dulwich:0.18.0 # 2017_11
hg:4.2 hggit:0.8.10 dulwich:0.18.0 # 2017_11
hg:4.3 hggit:0.8.10 dulwich:0.18.0 # 2017_11
hg:4.4 hggit:0.8.10 dulwich:0.18.0 # 2017_11
hg:4.5 hggit:0.8.11 dulwich:0.18.0 # 2018_02
hg:4.6 hggit:0.8.12 dulwich:0.19.7 # 2018_10
hg:4.7 hggit:0.8.12 dulwich:0.19.7 # 2018_10
hg:4.8 hggit:@ dulwich:0.19.11
hg:4.9 hggit:@ dulwich:0.19.11
hg:5.0 hggit:@ dulwich:0.19.11