* Re: [PATCH] tests: turn on test-lint-shell-syntax by default
From: Junio C Hamano @ 2013-01-13 22:38 UTC (permalink / raw)
To: Jonathan Nieder; +Cc: Torsten Bögershausen, git
In-Reply-To: <20130113173207.GC5973@elie.Belkin>
Jonathan Nieder <jrnieder@gmail.com> writes:
> Hi,
>
> Torsten Bögershausen wrote:
>
>> - /^\s*[^#]\s*which\s/ and err 'which is not portable (please use type)';
>> + /^\s*[^#]\s*which\s+[-a-zA-Z0-9]+$/ and err 'which is not portable (please use type)';
>
> Hmm. Neither the old version nor the new one matches what seem to
> be typical uses of 'which', based on a quick code search:
>
> if which sl >/dev/null 2>&1
> then
> sl -l
> ...
> fi
>
> or
>
> if test -x "$(which sl 2>/dev/null)"
> then
> sl -l
> ...
> fi
Yes, these two misuses are what we want it to trigger on, so the
test is very easy to trigger and produce a false positive, but does
not trigger on what we really want to catch.
That does not sound like a good benefit/cost ratio to me.
^ permalink raw reply
* Re: [PATCH v5] git-completion.bash: add support for path completion
From: Junio C Hamano @ 2013-01-13 22:46 UTC (permalink / raw)
To: Manlio Perillo; +Cc: git, szeder, felipe.contreras, peff
In-Reply-To: <50F15CB9.5090603@gmail.com>
Manlio Perillo <manlio.perillo@gmail.com> writes:
>> + # Skip "git" (first argument)
>> + for ((i=1; i < ${#words[@]}; i++)); do
>> + word="${words[i]}"
>> +
>> + case "$word" in
>> + --)
>
> Sorry, I have incorrectly (again) indented the case labels.
> I have now configured my editor to correctly indent this.
Yeah, thanks for spotting.
I wouldn't worry *too* much about the style in this script at this
point, though. It uses a style on its own that is totally different
from the rest of the system (e.g. "[" instead of "test", semicolon
in "if ...; then", etc.) and it probably is better to emulate the
surrounding code, and leave the style "fixes" to a separate topic,
if we want to (as a contrib/ material that is not POSIX but bash
specific, I do not know if that is even worth it).
>> + # Good; we can assume that the following are only non
>> + # option arguments.
>> + ((c = 0))
>> + ;;
>
> Here I was thinking to do something like this (not tested):
>
> -*)
> if [ -n ${2-} ]; then
> # Assume specified git command only
> # accepts simple options
> # (without arguments)
> ((c = 0))
>
> Since git mv only accepts simple options, this will make the use of '--'
> not required.
Unless you have a file whose name begins with a dash, perhaps?
^ permalink raw reply
* Re: [PATCH v5] git-completion.bash: add support for path completion
From: Junio C Hamano @ 2013-01-13 22:56 UTC (permalink / raw)
To: Manlio Perillo; +Cc: git, szeder, felipe.contreras, peff
In-Reply-To: <50F178C8.40806@gmail.com>
Manlio Perillo <manlio.perillo@gmail.com> writes:
> Il 11/01/2013 23:02, Junio C Hamano ha scritto:
>> Manlio Perillo <manlio.perillo@gmail.com> writes:
>>
>>> +# Process path list returned by "ls-files" and "diff-index --name-only"
>>> +# commands, in order to list only file names relative to a specified
>>> +# directory, and append a slash to directory names.
>>> +__git_index_file_list_filter ()
>>> +{
>>> + # Default to Bash >= 4.x
>>> + __git_index_file_list_filter_bash
>>> +}
>>> +
>>> +# Execute git ls-files, returning paths relative to the directory
>>> +# specified in the first argument, and using the options specified in
>>> +# the second argument.
>>> +__git_ls_files_helper ()
>>> +{
>>> + # NOTE: $2 is not quoted in order to support multiple options
>>> + cd "$1" && git ls-files --exclude-standard $2
>>> +} 2>/dev/null
>>
>> I think this redirection is correct but a bit tricky;
>
> It's not tricky: it is POSIX:
I know that. It is an instance of "Even it is in POSIX, we may want
to refrain using it, because some shells get it wrong, and it is
easy to work it around".
>> effect during the execution of the { block } (in other words, it is
>> not about squelching errors during the function definition).
>
> What do you mean by "squelching"?
Silencing, not showing the end user. Sending to /dev/null.
> I have added tcsh to the sh list, but it fails with:
> Badly placed ()'s.
tcsh (and csh) are not even in the Bourne shell family and is not
expected to be able to run any non trivial POSIX shell scripts. The
completion script for it does not dot-source this but instead lets
bash read it, so it is fine.
>> It however may affect zsh, which does seem to dot-source this file.
>> Perhaps zsh completion may have to be rewritten in a similar way as
>> tcsh completion is done (i.e. does not dot-source this file but ask
>> bash to do the heavy-lifting).
>
> Ok, I was wrong on assuming all modern shells were POSIX compliant.
Shells in csh family will never be, and being non-POSIX is not a
crime. It only matters when such a shell is allowed to dot-source
this script, and this script uses constructs that such a shell does
not understand.
> I will change the code to use a nested {} group.
>
>> This function seems to be always called in an subshell (e.g. as an
>> upstream of a pipeline), so the "cd" may be harmless, but don't you
>> need to disable CDPATH while doing this?
>
> I don't know.
The caller of this function figures out the value of $subdirname,
and calls you; your "cd $subdirname" may not go to ./$subdirname as
you expect, but to the $subdirname directory under one of the
directories listed in CDPATH, before running ls-tree or ls-files.
^ permalink raw reply
* Re: What's cooking in git.git (Jan 2013, #05; Fri, 11)
From: Junio C Hamano @ 2013-01-13 23:02 UTC (permalink / raw)
To: Duy Nguyen; +Cc: git
In-Reply-To: <CACsJy8CRbkLAD7LtoE_6FA_zW4YTW6Nb0mJU3ejqbu5URTrU1Q@mail.gmail.com>
Duy Nguyen <pclouds@gmail.com> writes:
> On Sat, Jan 12, 2013 at 6:56 AM, Junio C Hamano <gitster@pobox.com> wrote:
>> * nd/parse-pathspec (2013-01-11) 20 commits
>>
>> Uses the parsed pathspec structure in more places where we used to
>> use the raw "array of strings" pathspec.
>>
>> Unfortunately, this conflicts a couple of topics in flight. I tried
>> to be careful while resolving conflicts, though.
>
> parse_pathspec has not picked up init_pathspec changes from
> jk/pathspec-literal and nd/pathspec-wildcard (already in master) so
> --literal-pathspecs is probably broken in 'pu' after a lot of
> init_pathspec -> parse_pathspec conversion.
I guess it may be a better way forward to hold the series off, and
instead help polishing the other topics that are depended on so that
they can graduate sooner, given multiple topics in flight wants to
touch pathspecs (either change the way they are handled, or adds new
places that use them).
^ permalink raw reply
* Re: git-completion.bash should not add a space after a ref
From: Junio C Hamano @ 2013-01-13 23:16 UTC (permalink / raw)
To: Manlio Perillo; +Cc: git@vger.kernel.org, SZEDER Gábor, Felipe Contreras
In-Reply-To: <50F1AD0F.7080503@gmail.com>
Manlio Perillo <manlio.perillo@gmail.com> writes:
> This is not really a bug, but a small usability problem.
>
> When completing a reference, Bash will add a space after the reference name.
>
> As an example in:
>
> $git show master<TAB>
>
> The problem is that an user may want to show a tree or blog object from
> master:
>
> $git show master:git.c
Or the user may want to see only changes to the documentation, e.g.
$ git show master Documentation/
so removing a SP is a regression in the other way around.
Given that "refer to an object in a tree" is a much less often used
operation, I have a feeling that the current behaviour happens to
make a good trade-off between these two conflicting purposes that
cannot be satisfied both at the same time.
^ permalink raw reply
* Re: [PATCH v3 00/31] nd/parse-pathspec
From: Junio C Hamano @ 2013-01-13 23:21 UTC (permalink / raw)
To: Nguyễn Thái Ngọc Duy; +Cc: git
In-Reply-To: <1358080539-17436-1-git-send-email-pclouds@gmail.com>
Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:
> Changes from v2 (it's hard to keep track of after the rebase, so I may
> be missing something here):
>
> - rebased on top of recent master, incorporate changes in
> init_pathspec from jk/pathspec-literal and nd/pathspec-wildcard to
> parse_pathspec
>
> - kill strip_trailing_slash_from_submodules and treat_gitlinks
> (pretty sure it'll cause conflicts with as/check-ignore)
>
> - kill init_pathspec, match_pathspec, diff_tree_setup_paths and
> diff_tree_release_paths
>
> - check points for future pathspec development
>
> As far as I understand the "pathspec unification", I'd say we are
> there, with a few exceptions like "mv", external commands.. But those
> are pretty much isolated.
>
> I'll send another WIP series implementing :(icase) and :(glob), mainly
> to show (me) how future pathspec feature development looks like after
> this.
;-)
Thanks; looking forward to reading it.
^ permalink raw reply
* Re: [PATCH] t0050: mark TC merge (case change) as success
From: Junio C Hamano @ 2013-01-13 23:24 UTC (permalink / raw)
To: Torsten Bögershausen; +Cc: git
In-Reply-To: <201301132138.37154.tboegi@web.de>
Torsten Bögershausen <tboegi@web.de> writes:
> The test "merge (case change)" passes on a case ignoring file system
>
> Use test_expect_success to remove the "known breakage vanished"
>
> Signed-off-by: Torsten Bögershausen <tboegi@web.de>
> ---
Interesting. When did this change? Do you happen to have a
bisection? Or did the test pass from the very beginning?
> t/t0050-filesystem.sh | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/t/t0050-filesystem.sh b/t/t0050-filesystem.sh
> index 78816d9..ccd685d 100755
> --- a/t/t0050-filesystem.sh
> +++ b/t/t0050-filesystem.sh
> @@ -77,7 +77,7 @@ $test_case 'rename (case change)' '
>
> '
>
> -$test_case 'merge (case change)' '
> +test_expect_success 'merge (case change)' '
>
> rm -f CamelCase &&
> rm -f camelcase &&
^ permalink raw reply
* Re: [PATCH] cvsimport: rewrite to use cvsps 3.x to fix major bugs
From: Junio C Hamano @ 2013-01-13 23:27 UTC (permalink / raw)
To: Jonathan Nieder; +Cc: Michael Haggerty, Eric S. Raymond, git, Chris Rorvick
In-Reply-To: <7v8v7wiv3a.fsf@alter.siamese.dyndns.org>
Junio C Hamano <gitster@pobox.com> writes:
> Jonathan Nieder <jrnieder@gmail.com> writes:
>
>> Michael Haggerty wrote:
>>
>>> Regarding your claim that "within a few months the Perl git-cvsimport is
>>> going to cease even pretending to work": It might be that the old
>>> git-cvsimport will stop working *for people who upgrade to cvsps 3.x*.
>>> But it is not realistic to expect people to synchronize their git and
>>> cvsps version upgrades. It is even quite possible that this or that
>>> Linux distribution will package incompatible versions of the two packages.
>>
>> Moreover, I feel an obligation to point the following out:
>>
>> In a hypothetical world where cvsps 3.x simply breaks "git cvsimport"
>> it is likely that some distributions would just stick to the existing
>> cvsps and not upgrade to 3.x. Maybe that's a wrong choice, but that's
>> a choice some would make. An even more likely outcome in that
>> hypothetical world is that they would ship it renamed to something
>> like "cvsps3" alongside the existing cvsps. Or they could rename the
>> old version to "cvsps2". If we were the last holdout, we could even
>> bundle it as compat/cvsps.
>>
>> So please do not act as though the cvsps upgrade is a crisis that we
>> need to break ourselves for at threat of no longer working at all.
>> The threat doesn't hold water.
>>
>> Luckily you have already written patches to make "git cvsimport" work
>> with cvsps 3.x, and through your work you are making a better
>> argument: "The new cvsimport + cvsps will work better, at least for
>> some users, than the old tool."
>>
>> Just don't pretend you have the power to force a change for a less
>> sensible reason than that!
>
> After a quick survey of various distros, I think it is very unlikely
> that we will see "distros move on to newer cvsps, leaving cvsimport
> broken" situation. If anything, it is more like "distros decide to
> ignore the new cvsps, until it is made to work with cvsimport" [*1*].
>
> I think it is probably sensible to rename the current cvsimport to
> cvsimport-2, write a small wrapper git-cvsimport.sh which is
> something like this:
>
> ----- >8 -----
> #!/bin/sh
>
> if test -z "$GIT_CVSPS_VERSION"
> then
> case "$(cvsps -h 2>&1 | grep 'cvsps version')" in
> 2.*)
> GIT_CVSPS_VERSION=2
> ;;
> 3.*)
> GIT_CVSPS_VERSION=3
> ;;
> esac
> fi
>
> if test -z "$GIT_CVSPS_VERSION"
> then
> echo >&2 "No supported cvsps available"
> exit 1
> fi
>
> exec git cvsimport-$GIT_CVSPS_VERSION "$@"
> ----- 8< -----
>
> and put Eric's one as git-cvsimport-3 (after ripping out the code to
> fallback to the old cvsimport). The longer term trend will be to
> move away from cvsimport-2, as it is unlikely cvsps-2.x will gain
> improvements, if any; keeping fallback code outside cvsimport-3 will
> be a better first step in the healthier long term code evolution.
>
> We will keep the current t96xx series of tests, and have them export
> GIT_CVSPS_VERSION=2 at the beginning, protect them with test prereq
> that requires presence of cvsps 2.x; this will still make sure that
> the current cvsimport users will not see any regressions.
>
> Eric's one should be polished enough to produce good results on the
s/should be polished enough/should be in a polished enough state/
that is. Also "if not right now" may better convey what I meant if
written "if not already".
> simpler sample CVS histories t96xx deal with soonish if not right
> now, so we can use a method similar to how we shared tests between
> blame and annotate while both were _different_ implementations to
> make sure the newer blame did not inroduce regression by running the
> same set of tests. Where the result _ought_ to differ, we should
> also add tests that work only with the new cvsimport, of course.
>
> I could help getting the ball rolling on this, if everybody agrees
> that the above is a sensible direction to go, given the real world
> constraints of distro inertia.
>
> Agreed?
>
>
> [References]
>
> *1* Fedora, Debian and Ubuntu do not even have cvsps 3.x in their
> bleeding edges, OpenBSD and NetBSD do not seem to have it either,
> and Gentoo and ArchLinux have the cvsps 3.x blocked due to
> incompatiblity.
>
> http://pkgs.fedoraproject.org/cgit/cvsps.git/
> http://packages.debian.org/search?keywords=cvsps
> http://packages.ubuntu.com/search?keywords=cvsps
>
> http://packages.gentoo.org/package/dev-vcs/cvsps
> https://bugs.gentoo.org/show_bug.cgi?id=450424
>
> https://bugs.archlinux.org/task/33363?project=1&cat%5B0%5D=2&string=cvsps
^ permalink raw reply
* Re: [PATCH v3 03/31] Add parse_pathspec() that converts cmdline args to struct pathspec
From: Martin von Zweigbergk @ 2013-01-14 0:05 UTC (permalink / raw)
To: Nguyễn Thái Ngọc Duy; +Cc: git, Junio C Hamano
In-Reply-To: <1358080539-17436-4-git-send-email-pclouds@gmail.com>
On Sun, Jan 13, 2013 at 4:35 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
> +static void parse_pathspec(struct pathspec *pathspec,
> + unsigned magic_mask, unsigned flags,
> + const char *prefix, const char **argv)
> +{
> + struct pathspec_item *item;
> + const char *entry = *argv;
> ...
> + for (i = 0; i < n; i++) {
> + const char *arg = argv[i];
Nit: "*argv" was assigned to "entry" above. Reassign that variable instead?
^ permalink raw reply
* cvs-fast-export release announcement
From: Eric S. Raymond @ 2013-01-14 0:13 UTC (permalink / raw)
To: git
Version 0.2 of the code formerly known as parsecvs has just shipped as
cvs-fast-export. Project page, with links to documentation and the
public repository, is at <http://www.catb.org/esr/cvs-fast-export/>.
I have some cvsps and reposurgeon patches to merge before I can get
back to the script wrapper.
--
<a href="http://www.catb.org/~esr/">Eric S. Raymond</a>
The most foolish mistake we could possibly make would be to permit
the conquered Eastern peoples to have arms. History teaches that all
conquerors who have allowed their subject races to carry arms have
prepared their own downfall by doing so.
-- Adolph Hitler, April 11 1942.
^ permalink raw reply
* Re: [PATCH v3 03/31] Add parse_pathspec() that converts cmdline args to struct pathspec
From: Duy Nguyen @ 2013-01-14 1:11 UTC (permalink / raw)
To: Martin von Zweigbergk; +Cc: git, Junio C Hamano
In-Reply-To: <CANiSa6icv7V7hoEzHQT0mgqjCDcSkuLvZ2M=6Q5gp6NcXJ20jQ@mail.gmail.com>
On Mon, Jan 14, 2013 at 7:05 AM, Martin von Zweigbergk
<martinvonz@gmail.com> wrote:
> On Sun, Jan 13, 2013 at 4:35 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:
>> +static void parse_pathspec(struct pathspec *pathspec,
>> + unsigned magic_mask, unsigned flags,
>> + const char *prefix, const char **argv)
>> +{
>> + struct pathspec_item *item;
>> + const char *entry = *argv;
>> ...
>> + for (i = 0; i < n; i++) {
>> + const char *arg = argv[i];
>
> Nit: "*argv" was assigned to "entry" above. Reassign that variable instead?
Yeah. Thanks for catching.
--
Duy
^ permalink raw reply
* Re: What's cooking in git.git (Jan 2013, #05; Fri, 11)
From: Duy Nguyen @ 2013-01-14 1:13 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7vobgsheko.fsf@alter.siamese.dyndns.org>
On Mon, Jan 14, 2013 at 6:02 AM, Junio C Hamano <gitster@pobox.com> wrote:
> Duy Nguyen <pclouds@gmail.com> writes:
>
>> On Sat, Jan 12, 2013 at 6:56 AM, Junio C Hamano <gitster@pobox.com> wrote:
>>> * nd/parse-pathspec (2013-01-11) 20 commits
>>>
>>> Uses the parsed pathspec structure in more places where we used to
>>> use the raw "array of strings" pathspec.
>>>
>>> Unfortunately, this conflicts a couple of topics in flight. I tried
>>> to be careful while resolving conflicts, though.
>>
>> parse_pathspec has not picked up init_pathspec changes from
>> jk/pathspec-literal and nd/pathspec-wildcard (already in master) so
>> --literal-pathspecs is probably broken in 'pu' after a lot of
>> init_pathspec -> parse_pathspec conversion.
>
> I guess it may be a better way forward to hold the series off, and
> instead help polishing the other topics that are depended on so that
> they can graduate sooner, given multiple topics in flight wants to
> touch pathspecs (either change the way they are handled, or adds new
> places that use them).
No problem. Just tell me when you want me to flood git@vger again.
--
Duy
^ permalink raw reply
* [PATCH 0/3] A smoother transition plan for cvsimport
From: Junio C Hamano @ 2013-01-14 1:40 UTC (permalink / raw)
To: git; +Cc: jrnieder, mhagger, esr, chris
In-Reply-To: <7v8v7wiv3a.fsf@alter.siamese.dyndns.org>
So here is a start of how such a transition plan outlined in the
previous message may look like.
The first two are preparatory step to allow the current code to
still work even when cvsps2 and cvsps3 are both available on the
system.
The last patch is mostly for illustration purposes; the cvsimport-3.py
script it adds was taken from the patch Eric sent earlier and I relayed
to the list, and does not have later improvements in Eric's tree or
any of Chris's patches.
Junio C Hamano (3):
cvsimport: allow setting a custom cvsps (2.x) program name
cvsimport: introduce a version-switch wrapper
cvsimport: start adding cvsps 3.x support
.gitignore | 1 +
Makefile | 28 +-
git-cvsimport-2.perl | 1179 ++++++++++++++++++++++++++++++++++++++++++++++++++
git-cvsimport-3.py | 344 +++++++++++++++
git-cvsimport.perl | 1177 -------------------------------------------------
git-cvsimport.sh | 5 +
t/lib-cvs.sh | 4 +-
7 files changed, 1553 insertions(+), 1185 deletions(-)
create mode 100755 git-cvsimport-2.perl
create mode 100755 git-cvsimport-3.py
delete mode 100755 git-cvsimport.perl
create mode 100755 git-cvsimport.sh
--
1.8.1.421.g6236851
^ permalink raw reply
* [PATCH 1/3] cvsimport: allow setting a custom cvsps (2.x) program name
From: Junio C Hamano @ 2013-01-14 1:40 UTC (permalink / raw)
To: git; +Cc: jrnieder, mhagger, esr, chris
In-Reply-To: <1358127629-7500-1-git-send-email-gitster@pobox.com>
Distros may ship old cvsps under a different name, or the user may
install it outside the normal $PATH. Allow setting CVSPS2_PATH from
the build environment.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
Makefile | 9 +++++++--
git-cvsimport.perl | 4 +++-
t/lib-cvs.sh | 4 +++-
3 files changed, 13 insertions(+), 4 deletions(-)
diff --git a/Makefile b/Makefile
index 1b30d7b..8cb4a1b 100644
--- a/Makefile
+++ b/Makefile
@@ -571,9 +571,11 @@ endif
ifndef PYTHON_PATH
PYTHON_PATH = /usr/bin/python
endif
+ifndef CVSPS2_PATH
+ CVSPS2_PATH = cvsps
+endif
-export PERL_PATH
-export PYTHON_PATH
+export PERL_PATH PYTHON_PATH CVSPS2_PATH
LIB_FILE = libgit.a
XDIFF_LIB = xdiff/lib.a
@@ -1511,6 +1513,7 @@ SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
PYTHON_PATH_SQ = $(subst ','\'',$(PYTHON_PATH))
TCLTK_PATH_SQ = $(subst ','\'',$(TCLTK_PATH))
+CVSPS2_PATH_SQ = $(subst ','\'',$(CVSPS2_PATH))
DIFF_SQ = $(subst ','\'',$(DIFF))
LIBS = $(GITLIBS) $(EXTLIBS)
@@ -1724,6 +1727,7 @@ $(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl GIT-VERSION-FILE
-e ' H' \
-e ' x' \
-e '}' \
+ -e 's|@@CVSPS2_PATH@@|$(CVSPS2_PATH_SQ)|g' \
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
$@.perl >$@+ && \
chmod +x $@+ && \
@@ -2102,6 +2106,7 @@ GIT-LDFLAGS: FORCE
GIT-BUILD-OPTIONS: FORCE
@echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@
@echo PERL_PATH=\''$(subst ','\'',$(PERL_PATH_SQ))'\' >>$@
+ @echo CVSPS2_PATH=\''$(subst ','\'',$(CVSPS2_PATH_SQ))'\' >>$@
@echo DIFF=\''$(subst ','\'',$(subst ','\'',$(DIFF)))'\' >>$@
@echo PYTHON_PATH=\''$(subst ','\'',$(PYTHON_PATH_SQ))'\' >>$@
@echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@
diff --git a/git-cvsimport.perl b/git-cvsimport.perl
index 0a31ebd..ad460a5 100755
--- a/git-cvsimport.perl
+++ b/git-cvsimport.perl
@@ -33,6 +33,8 @@
our ($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,@opt_M,$opt_A,$opt_S,$opt_L, $opt_a, $opt_r, $opt_R);
my (%conv_author_name, %conv_author_email, %conv_author_tz);
+my $cvsps2 = "@@CVSPS2_PATH@@";
+
sub usage(;$) {
my $msg = shift;
print(STDERR "Error: $msg\n") if $msg;
@@ -751,7 +753,7 @@ sub munge_user_filename {
unless (defined($opt_p) && $opt_p =~ m/--no-cvs-direct/) {
push @opt, '--cvs-direct';
}
- exec("cvsps","--norc",@opt,"-u","-A",'--root',$opt_d,$cvs_tree);
+ exec($cvsps2,"--norc",@opt,"-u","-A",'--root',$opt_d,$cvs_tree);
die "Could not start cvsps: $!\n";
}
($cvspsfh, $cvspsfile) = tempfile('gitXXXXXX', SUFFIX => '.cvsps',
diff --git a/t/lib-cvs.sh b/t/lib-cvs.sh
index 44263ad..bdab63c 100644
--- a/t/lib-cvs.sh
+++ b/t/lib-cvs.sh
@@ -13,7 +13,9 @@ fi
CVS="cvs -f"
export CVS
-cvsps_version=`cvsps -h 2>&1 | sed -ne 's/cvsps version //p'`
+CVSPS="$CVSPS2_PATH"
+
+cvsps_version=`$CVSPS -h 2>&1 | sed -ne 's/cvsps version //p'`
case "$cvsps_version" in
2.1 | 2.2*)
;;
--
1.8.1.421.g6236851
^ permalink raw reply related
* [PATCH 2/3] cvsimport: introduce a version-switch wrapper
From: Junio C Hamano @ 2013-01-14 1:40 UTC (permalink / raw)
To: git; +Cc: jrnieder, mhagger, esr, chris
In-Reply-To: <1358127629-7500-1-git-send-email-gitster@pobox.com>
In preparation to have both old and new cvsimport during the
transition period, rename the cvsimport script to cvsimport-2
and introduce a small wrapper that we can later change it to
allow users to run either frontend (with their corresponding
cvsps backends).
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
.gitignore | 1 +
Makefile | 3 +-
git-cvsimport-2.perl | 1179 ++++++++++++++++++++++++++++++++++++++++++++++++++
git-cvsimport.perl | 1179 --------------------------------------------------
git-cvsimport.sh | 5 +
5 files changed, 1187 insertions(+), 1180 deletions(-)
create mode 100755 git-cvsimport-2.perl
delete mode 100755 git-cvsimport.perl
create mode 100755 git-cvsimport.sh
diff --git a/.gitignore b/.gitignore
index aa258a6..8cb799c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -40,6 +40,7 @@
/git-credential-store
/git-cvsexportcommit
/git-cvsimport
+/git-cvsimport-2
/git-cvsserver
/git-daemon
/git-diff
diff --git a/Makefile b/Makefile
index 8cb4a1b..b022db2 100644
--- a/Makefile
+++ b/Makefile
@@ -435,6 +435,7 @@ unexport CDPATH
SCRIPT_SH += git-am.sh
SCRIPT_SH += git-bisect.sh
+SCRIPT_SH += git-cvsimport.sh
SCRIPT_SH += git-difftool--helper.sh
SCRIPT_SH += git-filter-branch.sh
SCRIPT_SH += git-lost-found.sh
@@ -463,7 +464,7 @@ SCRIPT_PERL += git-add--interactive.perl
SCRIPT_PERL += git-difftool.perl
SCRIPT_PERL += git-archimport.perl
SCRIPT_PERL += git-cvsexportcommit.perl
-SCRIPT_PERL += git-cvsimport.perl
+SCRIPT_PERL += git-cvsimport-2.perl
SCRIPT_PERL += git-cvsserver.perl
SCRIPT_PERL += git-relink.perl
SCRIPT_PERL += git-send-email.perl
diff --git a/git-cvsimport-2.perl b/git-cvsimport-2.perl
new file mode 100755
index 0000000..ad460a5
--- /dev/null
+++ b/git-cvsimport-2.perl
@@ -0,0 +1,1179 @@
+#!/usr/bin/perl
+
+# This tool is copyright (c) 2005, Matthias Urlichs.
+# It is released under the Gnu Public License, version 2.
+#
+# The basic idea is to aggregate CVS check-ins into related changes.
+# Fortunately, "cvsps" does that for us; all we have to do is to parse
+# its output.
+#
+# Checking out the files is done by a single long-running CVS connection
+# / server process.
+#
+# The head revision is on branch "origin" by default.
+# You can change that with the '-o' option.
+
+use 5.008;
+use strict;
+use warnings;
+use Getopt::Long;
+use File::Spec;
+use File::Temp qw(tempfile tmpnam);
+use File::Path qw(mkpath);
+use File::Basename qw(basename dirname);
+use Time::Local;
+use IO::Socket;
+use IO::Pipe;
+use POSIX qw(strftime tzset dup2 ENOENT);
+use IPC::Open2;
+
+$SIG{'PIPE'}="IGNORE";
+set_timezone('UTC');
+
+our ($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,@opt_M,$opt_A,$opt_S,$opt_L, $opt_a, $opt_r, $opt_R);
+my (%conv_author_name, %conv_author_email, %conv_author_tz);
+
+my $cvsps2 = "@@CVSPS2_PATH@@";
+
+sub usage(;$) {
+ my $msg = shift;
+ print(STDERR "Error: $msg\n") if $msg;
+ print STDERR <<END;
+Usage: git cvsimport # fetch/update GIT from CVS
+ [-o branch-for-HEAD] [-h] [-v] [-d CVSROOT] [-A author-conv-file]
+ [-p opts-for-cvsps] [-P file] [-C GIT_repository] [-z fuzz] [-i] [-k]
+ [-u] [-s subst] [-a] [-m] [-M regex] [-S regex] [-L commitlimit]
+ [-r remote] [-R] [CVS_module]
+END
+ exit(1);
+}
+
+sub read_author_info($) {
+ my ($file) = @_;
+ my $user;
+ open my $f, '<', "$file" or die("Failed to open $file: $!\n");
+
+ while (<$f>) {
+ # Expected format is this:
+ # exon=Andreas Ericsson <ae@op5.se>
+ if (m/^(\S+?)\s*=\s*(.+?)\s*<(.+)>\s*$/) {
+ $user = $1;
+ $conv_author_name{$user} = $2;
+ $conv_author_email{$user} = $3;
+ }
+ # or with an optional timezone:
+ # spawn=Simon Pawn <spawn@frog-pond.org> America/Chicago
+ elsif (m/^(\S+?)\s*=\s*(.+?)\s*<(.+)>\s*(\S+?)\s*$/) {
+ $user = $1;
+ $conv_author_name{$user} = $2;
+ $conv_author_email{$user} = $3;
+ $conv_author_tz{$user} = $4;
+ }
+ # However, we also read from CVSROOT/users format
+ # to ease migration.
+ elsif (/^(\w+):(['"]?)(.+?)\2\s*$/) {
+ my $mapped;
+ ($user, $mapped) = ($1, $3);
+ if ($mapped =~ /^\s*(.*?)\s*<(.*)>\s*$/) {
+ $conv_author_name{$user} = $1;
+ $conv_author_email{$user} = $2;
+ }
+ elsif ($mapped =~ /^<?(.*)>?$/) {
+ $conv_author_name{$user} = $user;
+ $conv_author_email{$user} = $1;
+ }
+ }
+ # NEEDSWORK: Maybe warn on unrecognized lines?
+ }
+ close ($f);
+}
+
+sub write_author_info($) {
+ my ($file) = @_;
+ open my $f, '>', $file or
+ die("Failed to open $file for writing: $!");
+
+ foreach (keys %conv_author_name) {
+ print $f "$_=$conv_author_name{$_} <$conv_author_email{$_}>";
+ print $f " $conv_author_tz{$_}" if ($conv_author_tz{$_});
+ print $f "\n";
+ }
+ close ($f);
+}
+
+# Versions of perl before 5.10.0 may not automatically check $TZ each
+# time localtime is run (most platforms will do so only the first time).
+# We can work around this by using tzset() to update the internal
+# variable whenever we change the environment.
+sub set_timezone {
+ $ENV{TZ} = shift;
+ tzset();
+}
+
+# convert getopts specs for use by git config
+my %longmap = (
+ 'A:' => 'authors-file',
+ 'M:' => 'merge-regex',
+ 'P:' => undef,
+ 'R' => 'track-revisions',
+ 'S:' => 'ignore-paths',
+);
+
+sub read_repo_config {
+ # Split the string between characters, unless there is a ':'
+ # So "abc:de" becomes ["a", "b", "c:", "d", "e"]
+ my @opts = split(/ *(?!:)/, shift);
+ foreach my $o (@opts) {
+ my $key = $o;
+ $key =~ s/://g;
+ my $arg = 'git config';
+ $arg .= ' --bool' if ($o !~ /:$/);
+ my $ckey = $key;
+
+ if (exists $longmap{$o}) {
+ # An uppercase option like -R cannot be
+ # expressed in the configuration, as the
+ # variable names are downcased.
+ $ckey = $longmap{$o};
+ next if (! defined $ckey);
+ $ckey =~ s/-//g;
+ }
+ chomp(my $tmp = `$arg --get cvsimport.$ckey`);
+ if ($tmp && !($arg =~ /--bool/ && $tmp eq 'false')) {
+ no strict 'refs';
+ my $opt_name = "opt_" . $key;
+ if (!$$opt_name) {
+ $$opt_name = $tmp;
+ }
+ }
+ }
+}
+
+my $opts = "haivmkuo:d:p:r:C:z:s:M:P:A:S:L:R";
+read_repo_config($opts);
+Getopt::Long::Configure( 'no_ignore_case', 'bundling' );
+
+# turn the Getopt::Std specification in a Getopt::Long one,
+# with support for multiple -M options
+GetOptions( map { s/:/=s/; /M/ ? "$_\@" : $_ } split( /(?!:)/, $opts ) )
+ or usage();
+usage if $opt_h;
+
+if (@ARGV == 0) {
+ chomp(my $module = `git config --get cvsimport.module`);
+ push(@ARGV, $module) if $? == 0;
+}
+@ARGV <= 1 or usage("You can't specify more than one CVS module");
+
+if ($opt_d) {
+ $ENV{"CVSROOT"} = $opt_d;
+} elsif (-f 'CVS/Root') {
+ open my $f, '<', 'CVS/Root' or die 'Failed to open CVS/Root';
+ $opt_d = <$f>;
+ chomp $opt_d;
+ close $f;
+ $ENV{"CVSROOT"} = $opt_d;
+} elsif ($ENV{"CVSROOT"}) {
+ $opt_d = $ENV{"CVSROOT"};
+} else {
+ usage("CVSROOT needs to be set");
+}
+$opt_s ||= "-";
+$opt_a ||= 0;
+
+my $git_tree = $opt_C;
+$git_tree ||= ".";
+
+my $remote;
+if (defined $opt_r) {
+ $remote = 'refs/remotes/' . $opt_r;
+ $opt_o ||= "master";
+} else {
+ $opt_o ||= "origin";
+ $remote = 'refs/heads';
+}
+
+my $cvs_tree;
+if ($#ARGV == 0) {
+ $cvs_tree = $ARGV[0];
+} elsif (-f 'CVS/Repository') {
+ open my $f, '<', 'CVS/Repository' or
+ die 'Failed to open CVS/Repository';
+ $cvs_tree = <$f>;
+ chomp $cvs_tree;
+ close $f;
+} else {
+ usage("CVS module has to be specified");
+}
+
+our @mergerx = ();
+if ($opt_m) {
+ @mergerx = ( qr/\b(?:from|of|merge|merging|merged) ([-\w]+)/i );
+}
+if (@opt_M) {
+ push (@mergerx, map { qr/$_/ } @opt_M);
+}
+
+# Remember UTC of our starting time
+# we'll want to avoid importing commits
+# that are too recent
+our $starttime = time();
+
+select(STDERR); $|=1; select(STDOUT);
+
+
+package CVSconn;
+# Basic CVS dialog.
+# We're only interested in connecting and downloading, so ...
+
+use File::Spec;
+use File::Temp qw(tempfile);
+use POSIX qw(strftime dup2);
+
+sub new {
+ my ($what,$repo,$subdir) = @_;
+ $what=ref($what) if ref($what);
+
+ my $self = {};
+ $self->{'buffer'} = "";
+ bless($self,$what);
+
+ $repo =~ s#/+$##;
+ $self->{'fullrep'} = $repo;
+ $self->conn();
+
+ $self->{'subdir'} = $subdir;
+ $self->{'lines'} = undef;
+
+ return $self;
+}
+
+sub find_password_entry {
+ my ($cvspass, @cvsroot) = @_;
+ my ($file, $delim) = @$cvspass;
+ my $pass;
+ local ($_);
+
+ if (open(my $fh, $file)) {
+ # :pserver:cvs@mea.tmt.tele.fi:/cvsroot/zmailer Ah<Z
+ CVSPASSFILE:
+ while (<$fh>) {
+ chomp;
+ s/^\/\d+\s+//;
+ my ($w, $p) = split($delim,$_,2);
+ for my $cvsroot (@cvsroot) {
+ if ($w eq $cvsroot) {
+ $pass = $p;
+ last CVSPASSFILE;
+ }
+ }
+ }
+ close($fh);
+ }
+ return $pass;
+}
+
+sub conn {
+ my $self = shift;
+ my $repo = $self->{'fullrep'};
+ if ($repo =~ s/^:pserver(?:([^:]*)):(?:(.*?)(?::(.*?))?@)?([^:\/]*)(?::(\d*))?//) {
+ my ($param,$user,$pass,$serv,$port) = ($1,$2,$3,$4,$5);
+
+ my ($proxyhost,$proxyport);
+ if ($param && ($param =~ m/proxy=([^;]+)/)) {
+ $proxyhost = $1;
+ # Default proxyport, if not specified, is 8080.
+ $proxyport = 8080;
+ if ($ENV{"CVS_PROXY_PORT"}) {
+ $proxyport = $ENV{"CVS_PROXY_PORT"};
+ }
+ if ($param =~ m/proxyport=([^;]+)/) {
+ $proxyport = $1;
+ }
+ }
+ $repo ||= '/';
+
+ # if username is not explicit in CVSROOT, then use current user, as cvs would
+ $user=(getlogin() || $ENV{'LOGNAME'} || $ENV{'USER'} || "anonymous") unless $user;
+ my $rr2 = "-";
+ unless ($port) {
+ $rr2 = ":pserver:$user\@$serv:$repo";
+ $port=2401;
+ }
+ my $rr = ":pserver:$user\@$serv:$port$repo";
+
+ if ($pass) {
+ $pass = $self->_scramble($pass);
+ } else {
+ my @cvspass = ([$ENV{'HOME'}."/.cvspass", qr/\s/],
+ [$ENV{'HOME'}."/.cvs/cvspass", qr/=/]);
+ my @loc = ();
+ foreach my $cvspass (@cvspass) {
+ my $p = find_password_entry($cvspass, $rr, $rr2);
+ if ($p) {
+ push @loc, $cvspass->[0];
+ $pass = $p;
+ }
+ }
+
+ if (1 < @loc) {
+ die("Multiple cvs password files have ".
+ "entries for CVSROOT $opt_d: @loc");
+ } elsif (!$pass) {
+ $pass = "A";
+ }
+ }
+
+ my ($s, $rep);
+ if ($proxyhost) {
+
+ # Use a HTTP Proxy. Only works for HTTP proxies that
+ # don't require user authentication
+ #
+ # See: http://www.ietf.org/rfc/rfc2817.txt
+
+ $s = IO::Socket::INET->new(PeerHost => $proxyhost, PeerPort => $proxyport);
+ die "Socket to $proxyhost: $!\n" unless defined $s;
+ $s->write("CONNECT $serv:$port HTTP/1.1\r\nHost: $serv:$port\r\n\r\n")
+ or die "Write to $proxyhost: $!\n";
+ $s->flush();
+
+ $rep = <$s>;
+
+ # The answer should look like 'HTTP/1.x 2yy ....'
+ if (!($rep =~ m#^HTTP/1\.. 2[0-9][0-9]#)) {
+ die "Proxy connect: $rep\n";
+ }
+ # Skip up to the empty line of the proxy server output
+ # including the response headers.
+ while ($rep = <$s>) {
+ last if (!defined $rep ||
+ $rep eq "\n" ||
+ $rep eq "\r\n");
+ }
+ } else {
+ $s = IO::Socket::INET->new(PeerHost => $serv, PeerPort => $port);
+ die "Socket to $serv: $!\n" unless defined $s;
+ }
+
+ $s->write("BEGIN AUTH REQUEST\n$repo\n$user\n$pass\nEND AUTH REQUEST\n")
+ or die "Write to $serv: $!\n";
+ $s->flush();
+
+ $rep = <$s>;
+
+ if ($rep ne "I LOVE YOU\n") {
+ $rep="<unknown>" unless $rep;
+ die "AuthReply: $rep\n";
+ }
+ $self->{'socketo'} = $s;
+ $self->{'socketi'} = $s;
+ } else { # local or ext: Fork off our own cvs server.
+ my $pr = IO::Pipe->new();
+ my $pw = IO::Pipe->new();
+ my $pid = fork();
+ die "Fork: $!\n" unless defined $pid;
+ my $cvs = 'cvs';
+ $cvs = $ENV{CVS_SERVER} if exists $ENV{CVS_SERVER};
+ my $rsh = 'rsh';
+ $rsh = $ENV{CVS_RSH} if exists $ENV{CVS_RSH};
+
+ my @cvs = ($cvs, 'server');
+ my ($local, $user, $host);
+ $local = $repo =~ s/:local://;
+ if (!$local) {
+ $repo =~ s/:ext://;
+ $local = !($repo =~ s/^(?:([^\@:]+)\@)?([^:]+)://);
+ ($user, $host) = ($1, $2);
+ }
+ if (!$local) {
+ if ($user) {
+ unshift @cvs, $rsh, '-l', $user, $host;
+ } else {
+ unshift @cvs, $rsh, $host;
+ }
+ }
+
+ unless ($pid) {
+ $pr->writer();
+ $pw->reader();
+ dup2($pw->fileno(),0);
+ dup2($pr->fileno(),1);
+ $pr->close();
+ $pw->close();
+ exec(@cvs);
+ }
+ $pw->writer();
+ $pr->reader();
+ $self->{'socketo'} = $pw;
+ $self->{'socketi'} = $pr;
+ }
+ $self->{'socketo'}->write("Root $repo\n");
+
+ # Trial and error says that this probably is the minimum set
+ $self->{'socketo'}->write("Valid-responses ok error Valid-requests Mode M Mbinary E Checked-in Created Updated Merged Removed\n");
+
+ $self->{'socketo'}->write("valid-requests\n");
+ $self->{'socketo'}->flush();
+
+ my $rep=$self->readline();
+ die "Failed to read from server" unless defined $rep;
+ chomp($rep);
+ if ($rep !~ s/^Valid-requests\s*//) {
+ $rep="<unknown>" unless $rep;
+ die "Expected Valid-requests from server, but got: $rep\n";
+ }
+ chomp(my $res=$self->readline());
+ die "validReply: $res\n" if $res ne "ok";
+
+ $self->{'socketo'}->write("UseUnchanged\n") if $rep =~ /\bUseUnchanged\b/;
+ $self->{'repo'} = $repo;
+}
+
+sub readline {
+ my ($self) = @_;
+ return $self->{'socketi'}->getline();
+}
+
+sub _file {
+ # Request a file with a given revision.
+ # Trial and error says this is a good way to do it. :-/
+ my ($self,$fn,$rev) = @_;
+ $self->{'socketo'}->write("Argument -N\n") or return undef;
+ $self->{'socketo'}->write("Argument -P\n") or return undef;
+ # -kk: Linus' version doesn't use it - defaults to off
+ if ($opt_k) {
+ $self->{'socketo'}->write("Argument -kk\n") or return undef;
+ }
+ $self->{'socketo'}->write("Argument -r\n") or return undef;
+ $self->{'socketo'}->write("Argument $rev\n") or return undef;
+ $self->{'socketo'}->write("Argument --\n") or return undef;
+ $self->{'socketo'}->write("Argument $self->{'subdir'}/$fn\n") or return undef;
+ $self->{'socketo'}->write("Directory .\n") or return undef;
+ $self->{'socketo'}->write("$self->{'repo'}\n") or return undef;
+ # $self->{'socketo'}->write("Sticky T1.0\n") or return undef;
+ $self->{'socketo'}->write("co\n") or return undef;
+ $self->{'socketo'}->flush() or return undef;
+ $self->{'lines'} = 0;
+ return 1;
+}
+sub _line {
+ # Read a line from the server.
+ # ... except that 'line' may be an entire file. ;-)
+ my ($self, $fh) = @_;
+ die "Not in lines" unless defined $self->{'lines'};
+
+ my $line;
+ my $res=0;
+ while (defined($line = $self->readline())) {
+ # M U gnupg-cvs-rep/AUTHORS
+ # Updated gnupg-cvs-rep/
+ # /daten/src/rsync/gnupg-cvs-rep/AUTHORS
+ # /AUTHORS/1.1///T1.1
+ # u=rw,g=rw,o=rw
+ # 0
+ # ok
+
+ if ($line =~ s/^(?:Created|Updated) //) {
+ $line = $self->readline(); # path
+ $line = $self->readline(); # Entries line
+ my $mode = $self->readline(); chomp $mode;
+ $self->{'mode'} = $mode;
+ defined (my $cnt = $self->readline())
+ or die "EOF from server after 'Changed'\n";
+ chomp $cnt;
+ die "Duh: Filesize $cnt" if $cnt !~ /^\d+$/;
+ $line="";
+ $res = $self->_fetchfile($fh, $cnt);
+ } elsif ($line =~ s/^ //) {
+ print $fh $line;
+ $res += length($line);
+ } elsif ($line =~ /^M\b/) {
+ # output, do nothing
+ } elsif ($line =~ /^Mbinary\b/) {
+ my $cnt;
+ die "EOF from server after 'Mbinary'" unless defined ($cnt = $self->readline());
+ chomp $cnt;
+ die "Duh: Mbinary $cnt" if $cnt !~ /^\d+$/ or $cnt<1;
+ $line="";
+ $res += $self->_fetchfile($fh, $cnt);
+ } else {
+ chomp $line;
+ if ($line eq "ok") {
+ # print STDERR "S: ok (".length($res).")\n";
+ return $res;
+ } elsif ($line =~ s/^E //) {
+ # print STDERR "S: $line\n";
+ } elsif ($line =~ /^(Remove-entry|Removed) /i) {
+ $line = $self->readline(); # filename
+ $line = $self->readline(); # OK
+ chomp $line;
+ die "Unknown: $line" if $line ne "ok";
+ return -1;
+ } else {
+ die "Unknown: $line\n";
+ }
+ }
+ }
+ return undef;
+}
+sub file {
+ my ($self,$fn,$rev) = @_;
+ my $res;
+
+ my ($fh, $name) = tempfile('gitcvs.XXXXXX',
+ DIR => File::Spec->tmpdir(), UNLINK => 1);
+
+ $self->_file($fn,$rev) and $res = $self->_line($fh);
+
+ if (!defined $res) {
+ print STDERR "Server has gone away while fetching $fn $rev, retrying...\n";
+ truncate $fh, 0;
+ $self->conn();
+ $self->_file($fn,$rev) or die "No file command send";
+ $res = $self->_line($fh);
+ die "Retry failed" unless defined $res;
+ }
+ close ($fh);
+
+ return ($name, $res);
+}
+sub _fetchfile {
+ my ($self, $fh, $cnt) = @_;
+ my $res = 0;
+ my $bufsize = 1024 * 1024;
+ while ($cnt) {
+ if ($bufsize > $cnt) {
+ $bufsize = $cnt;
+ }
+ my $buf;
+ my $num = $self->{'socketi'}->read($buf,$bufsize);
+ die "Server: Filesize $cnt: $num: $!\n" if not defined $num or $num<=0;
+ print $fh $buf;
+ $res += $num;
+ $cnt -= $num;
+ }
+ return $res;
+}
+
+sub _scramble {
+ my ($self, $pass) = @_;
+ my $scrambled = "A";
+
+ return $scrambled unless $pass;
+
+ my $pass_len = length($pass);
+ my @pass_arr = split("", $pass);
+ my $i;
+
+ # from cvs/src/scramble.c
+ my @shifts = (
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 114,120, 53, 79, 96,109, 72,108, 70, 64, 76, 67,116, 74, 68, 87,
+ 111, 52, 75,119, 49, 34, 82, 81, 95, 65,112, 86,118,110,122,105,
+ 41, 57, 83, 43, 46,102, 40, 89, 38,103, 45, 50, 42,123, 91, 35,
+ 125, 55, 54, 66,124,126, 59, 47, 92, 71,115, 78, 88,107,106, 56,
+ 36,121,117,104,101,100, 69, 73, 99, 63, 94, 93, 39, 37, 61, 48,
+ 58,113, 32, 90, 44, 98, 60, 51, 33, 97, 62, 77, 84, 80, 85,223,
+ 225,216,187,166,229,189,222,188,141,249,148,200,184,136,248,190,
+ 199,170,181,204,138,232,218,183,255,234,220,247,213,203,226,193,
+ 174,172,228,252,217,201,131,230,197,211,145,238,161,179,160,212,
+ 207,221,254,173,202,146,224,151,140,196,205,130,135,133,143,246,
+ 192,159,244,239,185,168,215,144,139,165,180,157,147,186,214,176,
+ 227,231,219,169,175,156,206,198,129,164,150,210,154,177,134,127,
+ 182,128,158,208,162,132,167,209,149,241,153,251,237,236,171,195,
+ 243,233,253,240,194,250,191,155,142,137,245,235,163,242,178,152
+ );
+
+ for ($i = 0; $i < $pass_len; $i++) {
+ $scrambled .= pack("C", $shifts[ord($pass_arr[$i])]);
+ }
+
+ return $scrambled;
+}
+
+package main;
+
+my $cvs = CVSconn->new($opt_d, $cvs_tree);
+
+
+sub pdate($) {
+ my ($d) = @_;
+ m#(\d{2,4})/(\d\d)/(\d\d)\s(\d\d):(\d\d)(?::(\d\d))?#
+ or die "Unparseable date: $d\n";
+ my $y=$1; $y-=1900 if $y>1900;
+ return timegm($6||0,$5,$4,$3,$2-1,$y);
+}
+
+sub pmode($) {
+ my ($mode) = @_;
+ my $m = 0;
+ my $mm = 0;
+ my $um = 0;
+ for my $x(split(//,$mode)) {
+ if ($x eq ",") {
+ $m |= $mm&$um;
+ $mm = 0;
+ $um = 0;
+ } elsif ($x eq "u") { $um |= 0700;
+ } elsif ($x eq "g") { $um |= 0070;
+ } elsif ($x eq "o") { $um |= 0007;
+ } elsif ($x eq "r") { $mm |= 0444;
+ } elsif ($x eq "w") { $mm |= 0222;
+ } elsif ($x eq "x") { $mm |= 0111;
+ } elsif ($x eq "=") { # do nothing
+ } else { die "Unknown mode: $mode\n";
+ }
+ }
+ $m |= $mm&$um;
+ return $m;
+}
+
+sub getwd() {
+ my $pwd = `pwd`;
+ chomp $pwd;
+ return $pwd;
+}
+
+sub is_sha1 {
+ my $s = shift;
+ return $s =~ /^[a-f0-9]{40}$/;
+}
+
+sub get_headref ($) {
+ my $name = shift;
+ my $r = `git rev-parse --verify '$name' 2>/dev/null`;
+ return undef unless $? == 0;
+ chomp $r;
+ return $r;
+}
+
+my $user_filename_prepend = '';
+sub munge_user_filename {
+ my $name = shift;
+ return File::Spec->file_name_is_absolute($name) ?
+ $name :
+ $user_filename_prepend . $name;
+}
+
+-d $git_tree
+ or mkdir($git_tree,0777)
+ or die "Could not create $git_tree: $!";
+if ($git_tree ne '.') {
+ $user_filename_prepend = getwd() . '/';
+ chdir($git_tree);
+}
+
+my $last_branch = "";
+my $orig_branch = "";
+my %branch_date;
+my $tip_at_start = undef;
+
+my $git_dir = $ENV{"GIT_DIR"} || ".git";
+$git_dir = getwd()."/".$git_dir unless $git_dir =~ m#^/#;
+$ENV{"GIT_DIR"} = $git_dir;
+my $orig_git_index;
+$orig_git_index = $ENV{GIT_INDEX_FILE} if exists $ENV{GIT_INDEX_FILE};
+
+my %index; # holds filenames of one index per branch
+
+unless (-d $git_dir) {
+ system(qw(git init));
+ die "Cannot init the GIT db at $git_tree: $?\n" if $?;
+ system(qw(git read-tree --empty));
+ die "Cannot init an empty tree: $?\n" if $?;
+
+ $last_branch = $opt_o;
+ $orig_branch = "";
+} else {
+ open(F, "-|", qw(git symbolic-ref HEAD)) or
+ die "Cannot run git symbolic-ref: $!\n";
+ chomp ($last_branch = <F>);
+ $last_branch = basename($last_branch);
+ close(F);
+ unless ($last_branch) {
+ warn "Cannot read the last branch name: $! -- assuming 'master'\n";
+ $last_branch = "master";
+ }
+ $orig_branch = $last_branch;
+ $tip_at_start = `git rev-parse --verify HEAD`;
+
+ # Get the last import timestamps
+ my $fmt = '($ref, $author) = (%(refname), %(author));';
+ my @cmd = ('git', 'for-each-ref', '--perl', "--format=$fmt", $remote);
+ open(H, "-|", @cmd) or die "Cannot run git for-each-ref: $!\n";
+ while (defined(my $entry = <H>)) {
+ my ($ref, $author);
+ eval($entry) || die "cannot eval refs list: $@";
+ my ($head) = ($ref =~ m|^$remote/(.*)|);
+ $author =~ /^.*\s(\d+)\s[-+]\d{4}$/;
+ $branch_date{$head} = $1;
+ }
+ close(H);
+ if (!exists $branch_date{$opt_o}) {
+ die "Branch '$opt_o' does not exist.\n".
+ "Either use the correct '-o branch' option,\n".
+ "or import to a new repository.\n";
+ }
+}
+
+-d $git_dir
+ or die "Could not create git subdir ($git_dir).\n";
+
+# now we read (and possibly save) author-info as well
+-f "$git_dir/cvs-authors" and
+ read_author_info("$git_dir/cvs-authors");
+if ($opt_A) {
+ read_author_info(munge_user_filename($opt_A));
+ write_author_info("$git_dir/cvs-authors");
+}
+
+# open .git/cvs-revisions, if requested
+open my $revision_map, '>>', "$git_dir/cvs-revisions"
+ or die "Can't open $git_dir/cvs-revisions for appending: $!\n"
+ if defined $opt_R;
+
+
+#
+# run cvsps into a file unless we are getting
+# it passed as a file via $opt_P
+#
+my $cvspsfile;
+unless ($opt_P) {
+ print "Running cvsps...\n" if $opt_v;
+ my $pid = open(CVSPS,"-|");
+ my $cvspsfh;
+ die "Cannot fork: $!\n" unless defined $pid;
+ unless ($pid) {
+ my @opt;
+ @opt = split(/,/,$opt_p) if defined $opt_p;
+ unshift @opt, '-z', $opt_z if defined $opt_z;
+ unshift @opt, '-q' unless defined $opt_v;
+ unless (defined($opt_p) && $opt_p =~ m/--no-cvs-direct/) {
+ push @opt, '--cvs-direct';
+ }
+ exec($cvsps2,"--norc",@opt,"-u","-A",'--root',$opt_d,$cvs_tree);
+ die "Could not start cvsps: $!\n";
+ }
+ ($cvspsfh, $cvspsfile) = tempfile('gitXXXXXX', SUFFIX => '.cvsps',
+ DIR => File::Spec->tmpdir());
+ while (<CVSPS>) {
+ print $cvspsfh $_;
+ }
+ close CVSPS;
+ $? == 0 or die "git cvsimport: fatal: cvsps reported error\n";
+ close $cvspsfh;
+} else {
+ $cvspsfile = munge_user_filename($opt_P);
+}
+
+open(CVS, "<$cvspsfile") or die $!;
+
+## cvsps output:
+#---------------------
+#PatchSet 314
+#Date: 1999/09/18 13:03:59
+#Author: wkoch
+#Branch: STABLE-BRANCH-1-0
+#Ancestor branch: HEAD
+#Tag: (none)
+#Log:
+# See ChangeLog: Sat Sep 18 13:03:28 CEST 1999 Werner Koch
+#Members:
+# README:1.57->1.57.2.1
+# VERSION:1.96->1.96.2.1
+#
+#---------------------
+
+my $state = 0;
+
+sub update_index (\@\@) {
+ my $old = shift;
+ my $new = shift;
+ open(my $fh, '|-', qw(git update-index -z --index-info))
+ or die "unable to open git update-index: $!";
+ print $fh
+ (map { "0 0000000000000000000000000000000000000000\t$_\0" }
+ @$old),
+ (map { '100' . sprintf('%o', $_->[0]) . " $_->[1]\t$_->[2]\0" }
+ @$new)
+ or die "unable to write to git update-index: $!";
+ close $fh
+ or die "unable to write to git update-index: $!";
+ $? and die "git update-index reported error: $?";
+}
+
+sub write_tree () {
+ open(my $fh, '-|', qw(git write-tree))
+ or die "unable to open git write-tree: $!";
+ chomp(my $tree = <$fh>);
+ is_sha1($tree)
+ or die "Cannot get tree id ($tree): $!";
+ close($fh)
+ or die "Error running git write-tree: $?\n";
+ print "Tree ID $tree\n" if $opt_v;
+ return $tree;
+}
+
+my ($patchset,$date,$author_name,$author_email,$author_tz,$branch,$ancestor,$tag,$logmsg);
+my (@old,@new,@skipped,%ignorebranch,@commit_revisions);
+
+# commits that cvsps cannot place anywhere...
+$ignorebranch{'#CVSPS_NO_BRANCH'} = 1;
+
+sub commit {
+ if ($branch eq $opt_o && !$index{branch} &&
+ !get_headref("$remote/$branch")) {
+ # looks like an initial commit
+ # use the index primed by git init
+ $ENV{GIT_INDEX_FILE} = "$git_dir/index";
+ $index{$branch} = "$git_dir/index";
+ } else {
+ # use an index per branch to speed up
+ # imports of projects with many branches
+ unless ($index{$branch}) {
+ $index{$branch} = tmpnam();
+ $ENV{GIT_INDEX_FILE} = $index{$branch};
+ if ($ancestor) {
+ system("git", "read-tree", "$remote/$ancestor");
+ } else {
+ system("git", "read-tree", "$remote/$branch");
+ }
+ die "read-tree failed: $?\n" if $?;
+ }
+ }
+ $ENV{GIT_INDEX_FILE} = $index{$branch};
+
+ update_index(@old, @new);
+ @old = @new = ();
+ my $tree = write_tree();
+ my $parent = get_headref("$remote/$last_branch");
+ print "Parent ID " . ($parent ? $parent : "(empty)") . "\n" if $opt_v;
+
+ my @commit_args;
+ push @commit_args, ("-p", $parent) if $parent;
+
+ # loose detection of merges
+ # based on the commit msg
+ foreach my $rx (@mergerx) {
+ next unless $logmsg =~ $rx && $1;
+ my $mparent = $1 eq 'HEAD' ? $opt_o : $1;
+ if (my $sha1 = get_headref("$remote/$mparent")) {
+ push @commit_args, '-p', "$remote/$mparent";
+ print "Merge parent branch: $mparent\n" if $opt_v;
+ }
+ }
+
+ set_timezone($author_tz);
+ my $commit_date = strftime("%s %z", localtime($date));
+ set_timezone('UTC');
+ $ENV{GIT_AUTHOR_NAME} = $author_name;
+ $ENV{GIT_AUTHOR_EMAIL} = $author_email;
+ $ENV{GIT_AUTHOR_DATE} = $commit_date;
+ $ENV{GIT_COMMITTER_NAME} = $author_name;
+ $ENV{GIT_COMMITTER_EMAIL} = $author_email;
+ $ENV{GIT_COMMITTER_DATE} = $commit_date;
+ my $pid = open2(my $commit_read, my $commit_write,
+ 'git', 'commit-tree', $tree, @commit_args);
+
+ # compatibility with git2cvs
+ substr($logmsg,32767) = "" if length($logmsg) > 32767;
+ $logmsg =~ s/[\s\n]+\z//;
+
+ if (@skipped) {
+ $logmsg .= "\n\n\nSKIPPED:\n\t";
+ $logmsg .= join("\n\t", @skipped) . "\n";
+ @skipped = ();
+ }
+
+ print($commit_write "$logmsg\n") && close($commit_write)
+ or die "Error writing to git commit-tree: $!\n";
+
+ print "Committed patch $patchset ($branch $commit_date)\n" if $opt_v;
+ chomp(my $cid = <$commit_read>);
+ is_sha1($cid) or die "Cannot get commit id ($cid): $!\n";
+ print "Commit ID $cid\n" if $opt_v;
+ close($commit_read);
+
+ waitpid($pid,0);
+ die "Error running git commit-tree: $?\n" if $?;
+
+ system('git' , 'update-ref', "$remote/$branch", $cid) == 0
+ or die "Cannot write branch $branch for update: $!\n";
+
+ if ($revision_map) {
+ print $revision_map "@$_ $cid\n" for @commit_revisions;
+ }
+ @commit_revisions = ();
+
+ if ($tag) {
+ my ($xtag) = $tag;
+ $xtag =~ s/\s+\*\*.*$//; # Remove stuff like ** INVALID ** and ** FUNKY **
+ $xtag =~ tr/_/\./ if ( $opt_u );
+ $xtag =~ s/[\/]/$opt_s/g;
+
+ # See refs.c for these rules.
+ # Tag cannot contain bad chars. (See bad_ref_char in refs.c.)
+ $xtag =~ s/[ ~\^:\\\*\?\[]//g;
+ # Other bad strings for tags:
+ # (See check_refname_component in refs.c.)
+ 1 while $xtag =~ s/
+ (?: \.\. # Tag cannot contain '..'.
+ | \@{ # Tag cannot contain '@{'.
+ | ^ - # Tag cannot begin with '-'.
+ | \.lock $ # Tag cannot end with '.lock'.
+ | ^ \. # Tag cannot begin...
+ | \. $ # ...or end with '.'
+ )//xg;
+ # Tag cannot be empty.
+ if ($xtag eq '') {
+ warn("warning: ignoring tag '$tag'",
+ " with invalid tagname\n");
+ return;
+ }
+
+ if (system('git' , 'tag', '-f', $xtag, $cid) != 0) {
+ # We did our best to sanitize the tag, but still failed
+ # for whatever reason. Bail out, and give the user
+ # enough information to understand if/how we should
+ # improve the translation in the future.
+ if ($tag ne $xtag) {
+ print "Translated '$tag' tag to '$xtag'\n";
+ }
+ die "Cannot create tag $xtag: $!\n";
+ }
+
+ print "Created tag '$xtag' on '$branch'\n" if $opt_v;
+ }
+};
+
+my $commitcount = 1;
+while (<CVS>) {
+ chomp;
+ if ($state == 0 and /^-+$/) {
+ $state = 1;
+ } elsif ($state == 0) {
+ $state = 1;
+ redo;
+ } elsif (($state==0 or $state==1) and s/^PatchSet\s+//) {
+ $patchset = 0+$_;
+ $state=2;
+ } elsif ($state == 2 and s/^Date:\s+//) {
+ $date = pdate($_);
+ unless ($date) {
+ print STDERR "Could not parse date: $_\n";
+ $state=0;
+ next;
+ }
+ $state=3;
+ } elsif ($state == 3 and s/^Author:\s+//) {
+ $author_tz = "UTC";
+ s/\s+$//;
+ if (/^(.*?)\s+<(.*)>/) {
+ ($author_name, $author_email) = ($1, $2);
+ } elsif ($conv_author_name{$_}) {
+ $author_name = $conv_author_name{$_};
+ $author_email = $conv_author_email{$_};
+ $author_tz = $conv_author_tz{$_} if ($conv_author_tz{$_});
+ } else {
+ $author_name = $author_email = $_;
+ }
+ $state = 4;
+ } elsif ($state == 4 and s/^Branch:\s+//) {
+ s/\s+$//;
+ tr/_/\./ if ( $opt_u );
+ s/[\/]/$opt_s/g;
+ $branch = $_;
+ $state = 5;
+ } elsif ($state == 5 and s/^Ancestor branch:\s+//) {
+ s/\s+$//;
+ $ancestor = $_;
+ $ancestor = $opt_o if $ancestor eq "HEAD";
+ $state = 6;
+ } elsif ($state == 5) {
+ $ancestor = undef;
+ $state = 6;
+ redo;
+ } elsif ($state == 6 and s/^Tag:\s+//) {
+ s/\s+$//;
+ if ($_ eq "(none)") {
+ $tag = undef;
+ } else {
+ $tag = $_;
+ }
+ $state = 7;
+ } elsif ($state == 7 and /^Log:/) {
+ $logmsg = "";
+ $state = 8;
+ } elsif ($state == 8 and /^Members:/) {
+ $branch = $opt_o if $branch eq "HEAD";
+ if (defined $branch_date{$branch} and $branch_date{$branch} >= $date) {
+ # skip
+ print "skip patchset $patchset: $date before $branch_date{$branch}\n" if $opt_v;
+ $state = 11;
+ next;
+ }
+ if (!$opt_a && $starttime - 300 - (defined $opt_z ? $opt_z : 300) <= $date) {
+ # skip if the commit is too recent
+ # given that the cvsps default fuzz is 300s, we give ourselves another
+ # 300s just in case -- this also prevents skipping commits
+ # due to server clock drift
+ print "skip patchset $patchset: $date too recent\n" if $opt_v;
+ $state = 11;
+ next;
+ }
+ if (exists $ignorebranch{$branch}) {
+ print STDERR "Skipping $branch\n";
+ $state = 11;
+ next;
+ }
+ if ($ancestor) {
+ if ($ancestor eq $branch) {
+ print STDERR "Branch $branch erroneously stems from itself -- changed ancestor to $opt_o\n";
+ $ancestor = $opt_o;
+ }
+ if (defined get_headref("$remote/$branch")) {
+ print STDERR "Branch $branch already exists!\n";
+ $state=11;
+ next;
+ }
+ my $id = get_headref("$remote/$ancestor");
+ if (!$id) {
+ print STDERR "Branch $ancestor does not exist!\n";
+ $ignorebranch{$branch} = 1;
+ $state=11;
+ next;
+ }
+
+ system(qw(git update-ref -m cvsimport),
+ "$remote/$branch", $id);
+ if($? != 0) {
+ print STDERR "Could not create branch $branch\n";
+ $ignorebranch{$branch} = 1;
+ $state=11;
+ next;
+ }
+ }
+ $last_branch = $branch if $branch ne $last_branch;
+ $state = 9;
+ } elsif ($state == 8) {
+ $logmsg .= "$_\n";
+ } elsif ($state == 9 and /^\s+(.+?):(INITIAL|\d+(?:\.\d+)+)->(\d+(?:\.\d+)+)\s*$/) {
+# VERSION:1.96->1.96.2.1
+ my $init = ($2 eq "INITIAL");
+ my $fn = $1;
+ my $rev = $3;
+ $fn =~ s#^/+##;
+ if ($opt_S && $fn =~ m/$opt_S/) {
+ print "SKIPPING $fn v $rev\n";
+ push(@skipped, $fn);
+ next;
+ }
+ push @commit_revisions, [$fn, $rev];
+ print "Fetching $fn v $rev\n" if $opt_v;
+ my ($tmpname, $size) = $cvs->file($fn,$rev);
+ if ($size == -1) {
+ push(@old,$fn);
+ print "Drop $fn\n" if $opt_v;
+ } else {
+ print "".($init ? "New" : "Update")." $fn: $size bytes\n" if $opt_v;
+ my $pid = open(my $F, '-|');
+ die $! unless defined $pid;
+ if (!$pid) {
+ exec("git", "hash-object", "-w", $tmpname)
+ or die "Cannot create object: $!\n";
+ }
+ my $sha = <$F>;
+ chomp $sha;
+ close $F;
+ my $mode = pmode($cvs->{'mode'});
+ push(@new,[$mode, $sha, $fn]); # may be resurrected!
+ }
+ unlink($tmpname);
+ } elsif ($state == 9 and /^\s+(.+?):\d+(?:\.\d+)+->(\d+(?:\.\d+)+)\(DEAD\)\s*$/) {
+ my $fn = $1;
+ my $rev = $2;
+ $fn =~ s#^/+##;
+ push @commit_revisions, [$fn, $rev];
+ push(@old,$fn);
+ print "Delete $fn\n" if $opt_v;
+ } elsif ($state == 9 and /^\s*$/) {
+ $state = 10;
+ } elsif (($state == 9 or $state == 10) and /^-+$/) {
+ $commitcount++;
+ if ($opt_L && $commitcount > $opt_L) {
+ last;
+ }
+ commit();
+ if (($commitcount & 1023) == 0) {
+ system(qw(git repack -a -d));
+ }
+ $state = 1;
+ } elsif ($state == 11 and /^-+$/) {
+ $state = 1;
+ } elsif (/^-+$/) { # end of unknown-line processing
+ $state = 1;
+ } elsif ($state != 11) { # ignore stuff when skipping
+ print STDERR "* UNKNOWN LINE * $_\n";
+ }
+}
+commit() if $branch and $state != 11;
+
+unless ($opt_P) {
+ unlink($cvspsfile);
+}
+
+# The heuristic of repacking every 1024 commits can leave a
+# lot of unpacked data. If there is more than 1MB worth of
+# not-packed objects, repack once more.
+my $line = `git count-objects`;
+if ($line =~ /^(\d+) objects, (\d+) kilobytes$/) {
+ my ($n_objects, $kb) = ($1, $2);
+ 1024 < $kb
+ and system(qw(git repack -a -d));
+}
+
+foreach my $git_index (values %index) {
+ if ($git_index ne "$git_dir/index") {
+ unlink($git_index);
+ }
+}
+
+if (defined $orig_git_index) {
+ $ENV{GIT_INDEX_FILE} = $orig_git_index;
+} else {
+ delete $ENV{GIT_INDEX_FILE};
+}
+
+# Now switch back to the branch we were in before all of this happened
+if ($orig_branch) {
+ print "DONE.\n" if $opt_v;
+ if ($opt_i) {
+ exit 0;
+ }
+ my $tip_at_end = `git rev-parse --verify HEAD`;
+ if ($tip_at_start ne $tip_at_end) {
+ for ($tip_at_start, $tip_at_end) { chomp; }
+ print "Fetched into the current branch.\n" if $opt_v;
+ system(qw(git read-tree -u -m),
+ $tip_at_start, $tip_at_end);
+ die "Fast-forward update failed: $?\n" if $?;
+ }
+ else {
+ system(qw(git merge cvsimport HEAD), "$remote/$opt_o");
+ die "Could not merge $opt_o into the current branch.\n" if $?;
+ }
+} else {
+ $orig_branch = "master";
+ print "DONE; creating $orig_branch branch\n" if $opt_v;
+ system("git", "update-ref", "refs/heads/master", "$remote/$opt_o")
+ unless defined get_headref('refs/heads/master');
+ system("git", "symbolic-ref", "$remote/HEAD", "$remote/$opt_o")
+ if ($opt_r && $opt_o ne 'HEAD');
+ system('git', 'update-ref', 'HEAD', "$orig_branch");
+ unless ($opt_i) {
+ system(qw(git checkout -f));
+ die "checkout failed: $?\n" if $?;
+ }
+}
diff --git a/git-cvsimport.perl b/git-cvsimport.perl
deleted file mode 100755
index ad460a5..0000000
--- a/git-cvsimport.perl
+++ /dev/null
@@ -1,1179 +0,0 @@
-#!/usr/bin/perl
-
-# This tool is copyright (c) 2005, Matthias Urlichs.
-# It is released under the Gnu Public License, version 2.
-#
-# The basic idea is to aggregate CVS check-ins into related changes.
-# Fortunately, "cvsps" does that for us; all we have to do is to parse
-# its output.
-#
-# Checking out the files is done by a single long-running CVS connection
-# / server process.
-#
-# The head revision is on branch "origin" by default.
-# You can change that with the '-o' option.
-
-use 5.008;
-use strict;
-use warnings;
-use Getopt::Long;
-use File::Spec;
-use File::Temp qw(tempfile tmpnam);
-use File::Path qw(mkpath);
-use File::Basename qw(basename dirname);
-use Time::Local;
-use IO::Socket;
-use IO::Pipe;
-use POSIX qw(strftime tzset dup2 ENOENT);
-use IPC::Open2;
-
-$SIG{'PIPE'}="IGNORE";
-set_timezone('UTC');
-
-our ($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,@opt_M,$opt_A,$opt_S,$opt_L, $opt_a, $opt_r, $opt_R);
-my (%conv_author_name, %conv_author_email, %conv_author_tz);
-
-my $cvsps2 = "@@CVSPS2_PATH@@";
-
-sub usage(;$) {
- my $msg = shift;
- print(STDERR "Error: $msg\n") if $msg;
- print STDERR <<END;
-Usage: git cvsimport # fetch/update GIT from CVS
- [-o branch-for-HEAD] [-h] [-v] [-d CVSROOT] [-A author-conv-file]
- [-p opts-for-cvsps] [-P file] [-C GIT_repository] [-z fuzz] [-i] [-k]
- [-u] [-s subst] [-a] [-m] [-M regex] [-S regex] [-L commitlimit]
- [-r remote] [-R] [CVS_module]
-END
- exit(1);
-}
-
-sub read_author_info($) {
- my ($file) = @_;
- my $user;
- open my $f, '<', "$file" or die("Failed to open $file: $!\n");
-
- while (<$f>) {
- # Expected format is this:
- # exon=Andreas Ericsson <ae@op5.se>
- if (m/^(\S+?)\s*=\s*(.+?)\s*<(.+)>\s*$/) {
- $user = $1;
- $conv_author_name{$user} = $2;
- $conv_author_email{$user} = $3;
- }
- # or with an optional timezone:
- # spawn=Simon Pawn <spawn@frog-pond.org> America/Chicago
- elsif (m/^(\S+?)\s*=\s*(.+?)\s*<(.+)>\s*(\S+?)\s*$/) {
- $user = $1;
- $conv_author_name{$user} = $2;
- $conv_author_email{$user} = $3;
- $conv_author_tz{$user} = $4;
- }
- # However, we also read from CVSROOT/users format
- # to ease migration.
- elsif (/^(\w+):(['"]?)(.+?)\2\s*$/) {
- my $mapped;
- ($user, $mapped) = ($1, $3);
- if ($mapped =~ /^\s*(.*?)\s*<(.*)>\s*$/) {
- $conv_author_name{$user} = $1;
- $conv_author_email{$user} = $2;
- }
- elsif ($mapped =~ /^<?(.*)>?$/) {
- $conv_author_name{$user} = $user;
- $conv_author_email{$user} = $1;
- }
- }
- # NEEDSWORK: Maybe warn on unrecognized lines?
- }
- close ($f);
-}
-
-sub write_author_info($) {
- my ($file) = @_;
- open my $f, '>', $file or
- die("Failed to open $file for writing: $!");
-
- foreach (keys %conv_author_name) {
- print $f "$_=$conv_author_name{$_} <$conv_author_email{$_}>";
- print $f " $conv_author_tz{$_}" if ($conv_author_tz{$_});
- print $f "\n";
- }
- close ($f);
-}
-
-# Versions of perl before 5.10.0 may not automatically check $TZ each
-# time localtime is run (most platforms will do so only the first time).
-# We can work around this by using tzset() to update the internal
-# variable whenever we change the environment.
-sub set_timezone {
- $ENV{TZ} = shift;
- tzset();
-}
-
-# convert getopts specs for use by git config
-my %longmap = (
- 'A:' => 'authors-file',
- 'M:' => 'merge-regex',
- 'P:' => undef,
- 'R' => 'track-revisions',
- 'S:' => 'ignore-paths',
-);
-
-sub read_repo_config {
- # Split the string between characters, unless there is a ':'
- # So "abc:de" becomes ["a", "b", "c:", "d", "e"]
- my @opts = split(/ *(?!:)/, shift);
- foreach my $o (@opts) {
- my $key = $o;
- $key =~ s/://g;
- my $arg = 'git config';
- $arg .= ' --bool' if ($o !~ /:$/);
- my $ckey = $key;
-
- if (exists $longmap{$o}) {
- # An uppercase option like -R cannot be
- # expressed in the configuration, as the
- # variable names are downcased.
- $ckey = $longmap{$o};
- next if (! defined $ckey);
- $ckey =~ s/-//g;
- }
- chomp(my $tmp = `$arg --get cvsimport.$ckey`);
- if ($tmp && !($arg =~ /--bool/ && $tmp eq 'false')) {
- no strict 'refs';
- my $opt_name = "opt_" . $key;
- if (!$$opt_name) {
- $$opt_name = $tmp;
- }
- }
- }
-}
-
-my $opts = "haivmkuo:d:p:r:C:z:s:M:P:A:S:L:R";
-read_repo_config($opts);
-Getopt::Long::Configure( 'no_ignore_case', 'bundling' );
-
-# turn the Getopt::Std specification in a Getopt::Long one,
-# with support for multiple -M options
-GetOptions( map { s/:/=s/; /M/ ? "$_\@" : $_ } split( /(?!:)/, $opts ) )
- or usage();
-usage if $opt_h;
-
-if (@ARGV == 0) {
- chomp(my $module = `git config --get cvsimport.module`);
- push(@ARGV, $module) if $? == 0;
-}
-@ARGV <= 1 or usage("You can't specify more than one CVS module");
-
-if ($opt_d) {
- $ENV{"CVSROOT"} = $opt_d;
-} elsif (-f 'CVS/Root') {
- open my $f, '<', 'CVS/Root' or die 'Failed to open CVS/Root';
- $opt_d = <$f>;
- chomp $opt_d;
- close $f;
- $ENV{"CVSROOT"} = $opt_d;
-} elsif ($ENV{"CVSROOT"}) {
- $opt_d = $ENV{"CVSROOT"};
-} else {
- usage("CVSROOT needs to be set");
-}
-$opt_s ||= "-";
-$opt_a ||= 0;
-
-my $git_tree = $opt_C;
-$git_tree ||= ".";
-
-my $remote;
-if (defined $opt_r) {
- $remote = 'refs/remotes/' . $opt_r;
- $opt_o ||= "master";
-} else {
- $opt_o ||= "origin";
- $remote = 'refs/heads';
-}
-
-my $cvs_tree;
-if ($#ARGV == 0) {
- $cvs_tree = $ARGV[0];
-} elsif (-f 'CVS/Repository') {
- open my $f, '<', 'CVS/Repository' or
- die 'Failed to open CVS/Repository';
- $cvs_tree = <$f>;
- chomp $cvs_tree;
- close $f;
-} else {
- usage("CVS module has to be specified");
-}
-
-our @mergerx = ();
-if ($opt_m) {
- @mergerx = ( qr/\b(?:from|of|merge|merging|merged) ([-\w]+)/i );
-}
-if (@opt_M) {
- push (@mergerx, map { qr/$_/ } @opt_M);
-}
-
-# Remember UTC of our starting time
-# we'll want to avoid importing commits
-# that are too recent
-our $starttime = time();
-
-select(STDERR); $|=1; select(STDOUT);
-
-
-package CVSconn;
-# Basic CVS dialog.
-# We're only interested in connecting and downloading, so ...
-
-use File::Spec;
-use File::Temp qw(tempfile);
-use POSIX qw(strftime dup2);
-
-sub new {
- my ($what,$repo,$subdir) = @_;
- $what=ref($what) if ref($what);
-
- my $self = {};
- $self->{'buffer'} = "";
- bless($self,$what);
-
- $repo =~ s#/+$##;
- $self->{'fullrep'} = $repo;
- $self->conn();
-
- $self->{'subdir'} = $subdir;
- $self->{'lines'} = undef;
-
- return $self;
-}
-
-sub find_password_entry {
- my ($cvspass, @cvsroot) = @_;
- my ($file, $delim) = @$cvspass;
- my $pass;
- local ($_);
-
- if (open(my $fh, $file)) {
- # :pserver:cvs@mea.tmt.tele.fi:/cvsroot/zmailer Ah<Z
- CVSPASSFILE:
- while (<$fh>) {
- chomp;
- s/^\/\d+\s+//;
- my ($w, $p) = split($delim,$_,2);
- for my $cvsroot (@cvsroot) {
- if ($w eq $cvsroot) {
- $pass = $p;
- last CVSPASSFILE;
- }
- }
- }
- close($fh);
- }
- return $pass;
-}
-
-sub conn {
- my $self = shift;
- my $repo = $self->{'fullrep'};
- if ($repo =~ s/^:pserver(?:([^:]*)):(?:(.*?)(?::(.*?))?@)?([^:\/]*)(?::(\d*))?//) {
- my ($param,$user,$pass,$serv,$port) = ($1,$2,$3,$4,$5);
-
- my ($proxyhost,$proxyport);
- if ($param && ($param =~ m/proxy=([^;]+)/)) {
- $proxyhost = $1;
- # Default proxyport, if not specified, is 8080.
- $proxyport = 8080;
- if ($ENV{"CVS_PROXY_PORT"}) {
- $proxyport = $ENV{"CVS_PROXY_PORT"};
- }
- if ($param =~ m/proxyport=([^;]+)/) {
- $proxyport = $1;
- }
- }
- $repo ||= '/';
-
- # if username is not explicit in CVSROOT, then use current user, as cvs would
- $user=(getlogin() || $ENV{'LOGNAME'} || $ENV{'USER'} || "anonymous") unless $user;
- my $rr2 = "-";
- unless ($port) {
- $rr2 = ":pserver:$user\@$serv:$repo";
- $port=2401;
- }
- my $rr = ":pserver:$user\@$serv:$port$repo";
-
- if ($pass) {
- $pass = $self->_scramble($pass);
- } else {
- my @cvspass = ([$ENV{'HOME'}."/.cvspass", qr/\s/],
- [$ENV{'HOME'}."/.cvs/cvspass", qr/=/]);
- my @loc = ();
- foreach my $cvspass (@cvspass) {
- my $p = find_password_entry($cvspass, $rr, $rr2);
- if ($p) {
- push @loc, $cvspass->[0];
- $pass = $p;
- }
- }
-
- if (1 < @loc) {
- die("Multiple cvs password files have ".
- "entries for CVSROOT $opt_d: @loc");
- } elsif (!$pass) {
- $pass = "A";
- }
- }
-
- my ($s, $rep);
- if ($proxyhost) {
-
- # Use a HTTP Proxy. Only works for HTTP proxies that
- # don't require user authentication
- #
- # See: http://www.ietf.org/rfc/rfc2817.txt
-
- $s = IO::Socket::INET->new(PeerHost => $proxyhost, PeerPort => $proxyport);
- die "Socket to $proxyhost: $!\n" unless defined $s;
- $s->write("CONNECT $serv:$port HTTP/1.1\r\nHost: $serv:$port\r\n\r\n")
- or die "Write to $proxyhost: $!\n";
- $s->flush();
-
- $rep = <$s>;
-
- # The answer should look like 'HTTP/1.x 2yy ....'
- if (!($rep =~ m#^HTTP/1\.. 2[0-9][0-9]#)) {
- die "Proxy connect: $rep\n";
- }
- # Skip up to the empty line of the proxy server output
- # including the response headers.
- while ($rep = <$s>) {
- last if (!defined $rep ||
- $rep eq "\n" ||
- $rep eq "\r\n");
- }
- } else {
- $s = IO::Socket::INET->new(PeerHost => $serv, PeerPort => $port);
- die "Socket to $serv: $!\n" unless defined $s;
- }
-
- $s->write("BEGIN AUTH REQUEST\n$repo\n$user\n$pass\nEND AUTH REQUEST\n")
- or die "Write to $serv: $!\n";
- $s->flush();
-
- $rep = <$s>;
-
- if ($rep ne "I LOVE YOU\n") {
- $rep="<unknown>" unless $rep;
- die "AuthReply: $rep\n";
- }
- $self->{'socketo'} = $s;
- $self->{'socketi'} = $s;
- } else { # local or ext: Fork off our own cvs server.
- my $pr = IO::Pipe->new();
- my $pw = IO::Pipe->new();
- my $pid = fork();
- die "Fork: $!\n" unless defined $pid;
- my $cvs = 'cvs';
- $cvs = $ENV{CVS_SERVER} if exists $ENV{CVS_SERVER};
- my $rsh = 'rsh';
- $rsh = $ENV{CVS_RSH} if exists $ENV{CVS_RSH};
-
- my @cvs = ($cvs, 'server');
- my ($local, $user, $host);
- $local = $repo =~ s/:local://;
- if (!$local) {
- $repo =~ s/:ext://;
- $local = !($repo =~ s/^(?:([^\@:]+)\@)?([^:]+)://);
- ($user, $host) = ($1, $2);
- }
- if (!$local) {
- if ($user) {
- unshift @cvs, $rsh, '-l', $user, $host;
- } else {
- unshift @cvs, $rsh, $host;
- }
- }
-
- unless ($pid) {
- $pr->writer();
- $pw->reader();
- dup2($pw->fileno(),0);
- dup2($pr->fileno(),1);
- $pr->close();
- $pw->close();
- exec(@cvs);
- }
- $pw->writer();
- $pr->reader();
- $self->{'socketo'} = $pw;
- $self->{'socketi'} = $pr;
- }
- $self->{'socketo'}->write("Root $repo\n");
-
- # Trial and error says that this probably is the minimum set
- $self->{'socketo'}->write("Valid-responses ok error Valid-requests Mode M Mbinary E Checked-in Created Updated Merged Removed\n");
-
- $self->{'socketo'}->write("valid-requests\n");
- $self->{'socketo'}->flush();
-
- my $rep=$self->readline();
- die "Failed to read from server" unless defined $rep;
- chomp($rep);
- if ($rep !~ s/^Valid-requests\s*//) {
- $rep="<unknown>" unless $rep;
- die "Expected Valid-requests from server, but got: $rep\n";
- }
- chomp(my $res=$self->readline());
- die "validReply: $res\n" if $res ne "ok";
-
- $self->{'socketo'}->write("UseUnchanged\n") if $rep =~ /\bUseUnchanged\b/;
- $self->{'repo'} = $repo;
-}
-
-sub readline {
- my ($self) = @_;
- return $self->{'socketi'}->getline();
-}
-
-sub _file {
- # Request a file with a given revision.
- # Trial and error says this is a good way to do it. :-/
- my ($self,$fn,$rev) = @_;
- $self->{'socketo'}->write("Argument -N\n") or return undef;
- $self->{'socketo'}->write("Argument -P\n") or return undef;
- # -kk: Linus' version doesn't use it - defaults to off
- if ($opt_k) {
- $self->{'socketo'}->write("Argument -kk\n") or return undef;
- }
- $self->{'socketo'}->write("Argument -r\n") or return undef;
- $self->{'socketo'}->write("Argument $rev\n") or return undef;
- $self->{'socketo'}->write("Argument --\n") or return undef;
- $self->{'socketo'}->write("Argument $self->{'subdir'}/$fn\n") or return undef;
- $self->{'socketo'}->write("Directory .\n") or return undef;
- $self->{'socketo'}->write("$self->{'repo'}\n") or return undef;
- # $self->{'socketo'}->write("Sticky T1.0\n") or return undef;
- $self->{'socketo'}->write("co\n") or return undef;
- $self->{'socketo'}->flush() or return undef;
- $self->{'lines'} = 0;
- return 1;
-}
-sub _line {
- # Read a line from the server.
- # ... except that 'line' may be an entire file. ;-)
- my ($self, $fh) = @_;
- die "Not in lines" unless defined $self->{'lines'};
-
- my $line;
- my $res=0;
- while (defined($line = $self->readline())) {
- # M U gnupg-cvs-rep/AUTHORS
- # Updated gnupg-cvs-rep/
- # /daten/src/rsync/gnupg-cvs-rep/AUTHORS
- # /AUTHORS/1.1///T1.1
- # u=rw,g=rw,o=rw
- # 0
- # ok
-
- if ($line =~ s/^(?:Created|Updated) //) {
- $line = $self->readline(); # path
- $line = $self->readline(); # Entries line
- my $mode = $self->readline(); chomp $mode;
- $self->{'mode'} = $mode;
- defined (my $cnt = $self->readline())
- or die "EOF from server after 'Changed'\n";
- chomp $cnt;
- die "Duh: Filesize $cnt" if $cnt !~ /^\d+$/;
- $line="";
- $res = $self->_fetchfile($fh, $cnt);
- } elsif ($line =~ s/^ //) {
- print $fh $line;
- $res += length($line);
- } elsif ($line =~ /^M\b/) {
- # output, do nothing
- } elsif ($line =~ /^Mbinary\b/) {
- my $cnt;
- die "EOF from server after 'Mbinary'" unless defined ($cnt = $self->readline());
- chomp $cnt;
- die "Duh: Mbinary $cnt" if $cnt !~ /^\d+$/ or $cnt<1;
- $line="";
- $res += $self->_fetchfile($fh, $cnt);
- } else {
- chomp $line;
- if ($line eq "ok") {
- # print STDERR "S: ok (".length($res).")\n";
- return $res;
- } elsif ($line =~ s/^E //) {
- # print STDERR "S: $line\n";
- } elsif ($line =~ /^(Remove-entry|Removed) /i) {
- $line = $self->readline(); # filename
- $line = $self->readline(); # OK
- chomp $line;
- die "Unknown: $line" if $line ne "ok";
- return -1;
- } else {
- die "Unknown: $line\n";
- }
- }
- }
- return undef;
-}
-sub file {
- my ($self,$fn,$rev) = @_;
- my $res;
-
- my ($fh, $name) = tempfile('gitcvs.XXXXXX',
- DIR => File::Spec->tmpdir(), UNLINK => 1);
-
- $self->_file($fn,$rev) and $res = $self->_line($fh);
-
- if (!defined $res) {
- print STDERR "Server has gone away while fetching $fn $rev, retrying...\n";
- truncate $fh, 0;
- $self->conn();
- $self->_file($fn,$rev) or die "No file command send";
- $res = $self->_line($fh);
- die "Retry failed" unless defined $res;
- }
- close ($fh);
-
- return ($name, $res);
-}
-sub _fetchfile {
- my ($self, $fh, $cnt) = @_;
- my $res = 0;
- my $bufsize = 1024 * 1024;
- while ($cnt) {
- if ($bufsize > $cnt) {
- $bufsize = $cnt;
- }
- my $buf;
- my $num = $self->{'socketi'}->read($buf,$bufsize);
- die "Server: Filesize $cnt: $num: $!\n" if not defined $num or $num<=0;
- print $fh $buf;
- $res += $num;
- $cnt -= $num;
- }
- return $res;
-}
-
-sub _scramble {
- my ($self, $pass) = @_;
- my $scrambled = "A";
-
- return $scrambled unless $pass;
-
- my $pass_len = length($pass);
- my @pass_arr = split("", $pass);
- my $i;
-
- # from cvs/src/scramble.c
- my @shifts = (
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
- 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
- 114,120, 53, 79, 96,109, 72,108, 70, 64, 76, 67,116, 74, 68, 87,
- 111, 52, 75,119, 49, 34, 82, 81, 95, 65,112, 86,118,110,122,105,
- 41, 57, 83, 43, 46,102, 40, 89, 38,103, 45, 50, 42,123, 91, 35,
- 125, 55, 54, 66,124,126, 59, 47, 92, 71,115, 78, 88,107,106, 56,
- 36,121,117,104,101,100, 69, 73, 99, 63, 94, 93, 39, 37, 61, 48,
- 58,113, 32, 90, 44, 98, 60, 51, 33, 97, 62, 77, 84, 80, 85,223,
- 225,216,187,166,229,189,222,188,141,249,148,200,184,136,248,190,
- 199,170,181,204,138,232,218,183,255,234,220,247,213,203,226,193,
- 174,172,228,252,217,201,131,230,197,211,145,238,161,179,160,212,
- 207,221,254,173,202,146,224,151,140,196,205,130,135,133,143,246,
- 192,159,244,239,185,168,215,144,139,165,180,157,147,186,214,176,
- 227,231,219,169,175,156,206,198,129,164,150,210,154,177,134,127,
- 182,128,158,208,162,132,167,209,149,241,153,251,237,236,171,195,
- 243,233,253,240,194,250,191,155,142,137,245,235,163,242,178,152
- );
-
- for ($i = 0; $i < $pass_len; $i++) {
- $scrambled .= pack("C", $shifts[ord($pass_arr[$i])]);
- }
-
- return $scrambled;
-}
-
-package main;
-
-my $cvs = CVSconn->new($opt_d, $cvs_tree);
-
-
-sub pdate($) {
- my ($d) = @_;
- m#(\d{2,4})/(\d\d)/(\d\d)\s(\d\d):(\d\d)(?::(\d\d))?#
- or die "Unparseable date: $d\n";
- my $y=$1; $y-=1900 if $y>1900;
- return timegm($6||0,$5,$4,$3,$2-1,$y);
-}
-
-sub pmode($) {
- my ($mode) = @_;
- my $m = 0;
- my $mm = 0;
- my $um = 0;
- for my $x(split(//,$mode)) {
- if ($x eq ",") {
- $m |= $mm&$um;
- $mm = 0;
- $um = 0;
- } elsif ($x eq "u") { $um |= 0700;
- } elsif ($x eq "g") { $um |= 0070;
- } elsif ($x eq "o") { $um |= 0007;
- } elsif ($x eq "r") { $mm |= 0444;
- } elsif ($x eq "w") { $mm |= 0222;
- } elsif ($x eq "x") { $mm |= 0111;
- } elsif ($x eq "=") { # do nothing
- } else { die "Unknown mode: $mode\n";
- }
- }
- $m |= $mm&$um;
- return $m;
-}
-
-sub getwd() {
- my $pwd = `pwd`;
- chomp $pwd;
- return $pwd;
-}
-
-sub is_sha1 {
- my $s = shift;
- return $s =~ /^[a-f0-9]{40}$/;
-}
-
-sub get_headref ($) {
- my $name = shift;
- my $r = `git rev-parse --verify '$name' 2>/dev/null`;
- return undef unless $? == 0;
- chomp $r;
- return $r;
-}
-
-my $user_filename_prepend = '';
-sub munge_user_filename {
- my $name = shift;
- return File::Spec->file_name_is_absolute($name) ?
- $name :
- $user_filename_prepend . $name;
-}
-
--d $git_tree
- or mkdir($git_tree,0777)
- or die "Could not create $git_tree: $!";
-if ($git_tree ne '.') {
- $user_filename_prepend = getwd() . '/';
- chdir($git_tree);
-}
-
-my $last_branch = "";
-my $orig_branch = "";
-my %branch_date;
-my $tip_at_start = undef;
-
-my $git_dir = $ENV{"GIT_DIR"} || ".git";
-$git_dir = getwd()."/".$git_dir unless $git_dir =~ m#^/#;
-$ENV{"GIT_DIR"} = $git_dir;
-my $orig_git_index;
-$orig_git_index = $ENV{GIT_INDEX_FILE} if exists $ENV{GIT_INDEX_FILE};
-
-my %index; # holds filenames of one index per branch
-
-unless (-d $git_dir) {
- system(qw(git init));
- die "Cannot init the GIT db at $git_tree: $?\n" if $?;
- system(qw(git read-tree --empty));
- die "Cannot init an empty tree: $?\n" if $?;
-
- $last_branch = $opt_o;
- $orig_branch = "";
-} else {
- open(F, "-|", qw(git symbolic-ref HEAD)) or
- die "Cannot run git symbolic-ref: $!\n";
- chomp ($last_branch = <F>);
- $last_branch = basename($last_branch);
- close(F);
- unless ($last_branch) {
- warn "Cannot read the last branch name: $! -- assuming 'master'\n";
- $last_branch = "master";
- }
- $orig_branch = $last_branch;
- $tip_at_start = `git rev-parse --verify HEAD`;
-
- # Get the last import timestamps
- my $fmt = '($ref, $author) = (%(refname), %(author));';
- my @cmd = ('git', 'for-each-ref', '--perl', "--format=$fmt", $remote);
- open(H, "-|", @cmd) or die "Cannot run git for-each-ref: $!\n";
- while (defined(my $entry = <H>)) {
- my ($ref, $author);
- eval($entry) || die "cannot eval refs list: $@";
- my ($head) = ($ref =~ m|^$remote/(.*)|);
- $author =~ /^.*\s(\d+)\s[-+]\d{4}$/;
- $branch_date{$head} = $1;
- }
- close(H);
- if (!exists $branch_date{$opt_o}) {
- die "Branch '$opt_o' does not exist.\n".
- "Either use the correct '-o branch' option,\n".
- "or import to a new repository.\n";
- }
-}
-
--d $git_dir
- or die "Could not create git subdir ($git_dir).\n";
-
-# now we read (and possibly save) author-info as well
--f "$git_dir/cvs-authors" and
- read_author_info("$git_dir/cvs-authors");
-if ($opt_A) {
- read_author_info(munge_user_filename($opt_A));
- write_author_info("$git_dir/cvs-authors");
-}
-
-# open .git/cvs-revisions, if requested
-open my $revision_map, '>>', "$git_dir/cvs-revisions"
- or die "Can't open $git_dir/cvs-revisions for appending: $!\n"
- if defined $opt_R;
-
-
-#
-# run cvsps into a file unless we are getting
-# it passed as a file via $opt_P
-#
-my $cvspsfile;
-unless ($opt_P) {
- print "Running cvsps...\n" if $opt_v;
- my $pid = open(CVSPS,"-|");
- my $cvspsfh;
- die "Cannot fork: $!\n" unless defined $pid;
- unless ($pid) {
- my @opt;
- @opt = split(/,/,$opt_p) if defined $opt_p;
- unshift @opt, '-z', $opt_z if defined $opt_z;
- unshift @opt, '-q' unless defined $opt_v;
- unless (defined($opt_p) && $opt_p =~ m/--no-cvs-direct/) {
- push @opt, '--cvs-direct';
- }
- exec($cvsps2,"--norc",@opt,"-u","-A",'--root',$opt_d,$cvs_tree);
- die "Could not start cvsps: $!\n";
- }
- ($cvspsfh, $cvspsfile) = tempfile('gitXXXXXX', SUFFIX => '.cvsps',
- DIR => File::Spec->tmpdir());
- while (<CVSPS>) {
- print $cvspsfh $_;
- }
- close CVSPS;
- $? == 0 or die "git cvsimport: fatal: cvsps reported error\n";
- close $cvspsfh;
-} else {
- $cvspsfile = munge_user_filename($opt_P);
-}
-
-open(CVS, "<$cvspsfile") or die $!;
-
-## cvsps output:
-#---------------------
-#PatchSet 314
-#Date: 1999/09/18 13:03:59
-#Author: wkoch
-#Branch: STABLE-BRANCH-1-0
-#Ancestor branch: HEAD
-#Tag: (none)
-#Log:
-# See ChangeLog: Sat Sep 18 13:03:28 CEST 1999 Werner Koch
-#Members:
-# README:1.57->1.57.2.1
-# VERSION:1.96->1.96.2.1
-#
-#---------------------
-
-my $state = 0;
-
-sub update_index (\@\@) {
- my $old = shift;
- my $new = shift;
- open(my $fh, '|-', qw(git update-index -z --index-info))
- or die "unable to open git update-index: $!";
- print $fh
- (map { "0 0000000000000000000000000000000000000000\t$_\0" }
- @$old),
- (map { '100' . sprintf('%o', $_->[0]) . " $_->[1]\t$_->[2]\0" }
- @$new)
- or die "unable to write to git update-index: $!";
- close $fh
- or die "unable to write to git update-index: $!";
- $? and die "git update-index reported error: $?";
-}
-
-sub write_tree () {
- open(my $fh, '-|', qw(git write-tree))
- or die "unable to open git write-tree: $!";
- chomp(my $tree = <$fh>);
- is_sha1($tree)
- or die "Cannot get tree id ($tree): $!";
- close($fh)
- or die "Error running git write-tree: $?\n";
- print "Tree ID $tree\n" if $opt_v;
- return $tree;
-}
-
-my ($patchset,$date,$author_name,$author_email,$author_tz,$branch,$ancestor,$tag,$logmsg);
-my (@old,@new,@skipped,%ignorebranch,@commit_revisions);
-
-# commits that cvsps cannot place anywhere...
-$ignorebranch{'#CVSPS_NO_BRANCH'} = 1;
-
-sub commit {
- if ($branch eq $opt_o && !$index{branch} &&
- !get_headref("$remote/$branch")) {
- # looks like an initial commit
- # use the index primed by git init
- $ENV{GIT_INDEX_FILE} = "$git_dir/index";
- $index{$branch} = "$git_dir/index";
- } else {
- # use an index per branch to speed up
- # imports of projects with many branches
- unless ($index{$branch}) {
- $index{$branch} = tmpnam();
- $ENV{GIT_INDEX_FILE} = $index{$branch};
- if ($ancestor) {
- system("git", "read-tree", "$remote/$ancestor");
- } else {
- system("git", "read-tree", "$remote/$branch");
- }
- die "read-tree failed: $?\n" if $?;
- }
- }
- $ENV{GIT_INDEX_FILE} = $index{$branch};
-
- update_index(@old, @new);
- @old = @new = ();
- my $tree = write_tree();
- my $parent = get_headref("$remote/$last_branch");
- print "Parent ID " . ($parent ? $parent : "(empty)") . "\n" if $opt_v;
-
- my @commit_args;
- push @commit_args, ("-p", $parent) if $parent;
-
- # loose detection of merges
- # based on the commit msg
- foreach my $rx (@mergerx) {
- next unless $logmsg =~ $rx && $1;
- my $mparent = $1 eq 'HEAD' ? $opt_o : $1;
- if (my $sha1 = get_headref("$remote/$mparent")) {
- push @commit_args, '-p', "$remote/$mparent";
- print "Merge parent branch: $mparent\n" if $opt_v;
- }
- }
-
- set_timezone($author_tz);
- my $commit_date = strftime("%s %z", localtime($date));
- set_timezone('UTC');
- $ENV{GIT_AUTHOR_NAME} = $author_name;
- $ENV{GIT_AUTHOR_EMAIL} = $author_email;
- $ENV{GIT_AUTHOR_DATE} = $commit_date;
- $ENV{GIT_COMMITTER_NAME} = $author_name;
- $ENV{GIT_COMMITTER_EMAIL} = $author_email;
- $ENV{GIT_COMMITTER_DATE} = $commit_date;
- my $pid = open2(my $commit_read, my $commit_write,
- 'git', 'commit-tree', $tree, @commit_args);
-
- # compatibility with git2cvs
- substr($logmsg,32767) = "" if length($logmsg) > 32767;
- $logmsg =~ s/[\s\n]+\z//;
-
- if (@skipped) {
- $logmsg .= "\n\n\nSKIPPED:\n\t";
- $logmsg .= join("\n\t", @skipped) . "\n";
- @skipped = ();
- }
-
- print($commit_write "$logmsg\n") && close($commit_write)
- or die "Error writing to git commit-tree: $!\n";
-
- print "Committed patch $patchset ($branch $commit_date)\n" if $opt_v;
- chomp(my $cid = <$commit_read>);
- is_sha1($cid) or die "Cannot get commit id ($cid): $!\n";
- print "Commit ID $cid\n" if $opt_v;
- close($commit_read);
-
- waitpid($pid,0);
- die "Error running git commit-tree: $?\n" if $?;
-
- system('git' , 'update-ref', "$remote/$branch", $cid) == 0
- or die "Cannot write branch $branch for update: $!\n";
-
- if ($revision_map) {
- print $revision_map "@$_ $cid\n" for @commit_revisions;
- }
- @commit_revisions = ();
-
- if ($tag) {
- my ($xtag) = $tag;
- $xtag =~ s/\s+\*\*.*$//; # Remove stuff like ** INVALID ** and ** FUNKY **
- $xtag =~ tr/_/\./ if ( $opt_u );
- $xtag =~ s/[\/]/$opt_s/g;
-
- # See refs.c for these rules.
- # Tag cannot contain bad chars. (See bad_ref_char in refs.c.)
- $xtag =~ s/[ ~\^:\\\*\?\[]//g;
- # Other bad strings for tags:
- # (See check_refname_component in refs.c.)
- 1 while $xtag =~ s/
- (?: \.\. # Tag cannot contain '..'.
- | \@{ # Tag cannot contain '@{'.
- | ^ - # Tag cannot begin with '-'.
- | \.lock $ # Tag cannot end with '.lock'.
- | ^ \. # Tag cannot begin...
- | \. $ # ...or end with '.'
- )//xg;
- # Tag cannot be empty.
- if ($xtag eq '') {
- warn("warning: ignoring tag '$tag'",
- " with invalid tagname\n");
- return;
- }
-
- if (system('git' , 'tag', '-f', $xtag, $cid) != 0) {
- # We did our best to sanitize the tag, but still failed
- # for whatever reason. Bail out, and give the user
- # enough information to understand if/how we should
- # improve the translation in the future.
- if ($tag ne $xtag) {
- print "Translated '$tag' tag to '$xtag'\n";
- }
- die "Cannot create tag $xtag: $!\n";
- }
-
- print "Created tag '$xtag' on '$branch'\n" if $opt_v;
- }
-};
-
-my $commitcount = 1;
-while (<CVS>) {
- chomp;
- if ($state == 0 and /^-+$/) {
- $state = 1;
- } elsif ($state == 0) {
- $state = 1;
- redo;
- } elsif (($state==0 or $state==1) and s/^PatchSet\s+//) {
- $patchset = 0+$_;
- $state=2;
- } elsif ($state == 2 and s/^Date:\s+//) {
- $date = pdate($_);
- unless ($date) {
- print STDERR "Could not parse date: $_\n";
- $state=0;
- next;
- }
- $state=3;
- } elsif ($state == 3 and s/^Author:\s+//) {
- $author_tz = "UTC";
- s/\s+$//;
- if (/^(.*?)\s+<(.*)>/) {
- ($author_name, $author_email) = ($1, $2);
- } elsif ($conv_author_name{$_}) {
- $author_name = $conv_author_name{$_};
- $author_email = $conv_author_email{$_};
- $author_tz = $conv_author_tz{$_} if ($conv_author_tz{$_});
- } else {
- $author_name = $author_email = $_;
- }
- $state = 4;
- } elsif ($state == 4 and s/^Branch:\s+//) {
- s/\s+$//;
- tr/_/\./ if ( $opt_u );
- s/[\/]/$opt_s/g;
- $branch = $_;
- $state = 5;
- } elsif ($state == 5 and s/^Ancestor branch:\s+//) {
- s/\s+$//;
- $ancestor = $_;
- $ancestor = $opt_o if $ancestor eq "HEAD";
- $state = 6;
- } elsif ($state == 5) {
- $ancestor = undef;
- $state = 6;
- redo;
- } elsif ($state == 6 and s/^Tag:\s+//) {
- s/\s+$//;
- if ($_ eq "(none)") {
- $tag = undef;
- } else {
- $tag = $_;
- }
- $state = 7;
- } elsif ($state == 7 and /^Log:/) {
- $logmsg = "";
- $state = 8;
- } elsif ($state == 8 and /^Members:/) {
- $branch = $opt_o if $branch eq "HEAD";
- if (defined $branch_date{$branch} and $branch_date{$branch} >= $date) {
- # skip
- print "skip patchset $patchset: $date before $branch_date{$branch}\n" if $opt_v;
- $state = 11;
- next;
- }
- if (!$opt_a && $starttime - 300 - (defined $opt_z ? $opt_z : 300) <= $date) {
- # skip if the commit is too recent
- # given that the cvsps default fuzz is 300s, we give ourselves another
- # 300s just in case -- this also prevents skipping commits
- # due to server clock drift
- print "skip patchset $patchset: $date too recent\n" if $opt_v;
- $state = 11;
- next;
- }
- if (exists $ignorebranch{$branch}) {
- print STDERR "Skipping $branch\n";
- $state = 11;
- next;
- }
- if ($ancestor) {
- if ($ancestor eq $branch) {
- print STDERR "Branch $branch erroneously stems from itself -- changed ancestor to $opt_o\n";
- $ancestor = $opt_o;
- }
- if (defined get_headref("$remote/$branch")) {
- print STDERR "Branch $branch already exists!\n";
- $state=11;
- next;
- }
- my $id = get_headref("$remote/$ancestor");
- if (!$id) {
- print STDERR "Branch $ancestor does not exist!\n";
- $ignorebranch{$branch} = 1;
- $state=11;
- next;
- }
-
- system(qw(git update-ref -m cvsimport),
- "$remote/$branch", $id);
- if($? != 0) {
- print STDERR "Could not create branch $branch\n";
- $ignorebranch{$branch} = 1;
- $state=11;
- next;
- }
- }
- $last_branch = $branch if $branch ne $last_branch;
- $state = 9;
- } elsif ($state == 8) {
- $logmsg .= "$_\n";
- } elsif ($state == 9 and /^\s+(.+?):(INITIAL|\d+(?:\.\d+)+)->(\d+(?:\.\d+)+)\s*$/) {
-# VERSION:1.96->1.96.2.1
- my $init = ($2 eq "INITIAL");
- my $fn = $1;
- my $rev = $3;
- $fn =~ s#^/+##;
- if ($opt_S && $fn =~ m/$opt_S/) {
- print "SKIPPING $fn v $rev\n";
- push(@skipped, $fn);
- next;
- }
- push @commit_revisions, [$fn, $rev];
- print "Fetching $fn v $rev\n" if $opt_v;
- my ($tmpname, $size) = $cvs->file($fn,$rev);
- if ($size == -1) {
- push(@old,$fn);
- print "Drop $fn\n" if $opt_v;
- } else {
- print "".($init ? "New" : "Update")." $fn: $size bytes\n" if $opt_v;
- my $pid = open(my $F, '-|');
- die $! unless defined $pid;
- if (!$pid) {
- exec("git", "hash-object", "-w", $tmpname)
- or die "Cannot create object: $!\n";
- }
- my $sha = <$F>;
- chomp $sha;
- close $F;
- my $mode = pmode($cvs->{'mode'});
- push(@new,[$mode, $sha, $fn]); # may be resurrected!
- }
- unlink($tmpname);
- } elsif ($state == 9 and /^\s+(.+?):\d+(?:\.\d+)+->(\d+(?:\.\d+)+)\(DEAD\)\s*$/) {
- my $fn = $1;
- my $rev = $2;
- $fn =~ s#^/+##;
- push @commit_revisions, [$fn, $rev];
- push(@old,$fn);
- print "Delete $fn\n" if $opt_v;
- } elsif ($state == 9 and /^\s*$/) {
- $state = 10;
- } elsif (($state == 9 or $state == 10) and /^-+$/) {
- $commitcount++;
- if ($opt_L && $commitcount > $opt_L) {
- last;
- }
- commit();
- if (($commitcount & 1023) == 0) {
- system(qw(git repack -a -d));
- }
- $state = 1;
- } elsif ($state == 11 and /^-+$/) {
- $state = 1;
- } elsif (/^-+$/) { # end of unknown-line processing
- $state = 1;
- } elsif ($state != 11) { # ignore stuff when skipping
- print STDERR "* UNKNOWN LINE * $_\n";
- }
-}
-commit() if $branch and $state != 11;
-
-unless ($opt_P) {
- unlink($cvspsfile);
-}
-
-# The heuristic of repacking every 1024 commits can leave a
-# lot of unpacked data. If there is more than 1MB worth of
-# not-packed objects, repack once more.
-my $line = `git count-objects`;
-if ($line =~ /^(\d+) objects, (\d+) kilobytes$/) {
- my ($n_objects, $kb) = ($1, $2);
- 1024 < $kb
- and system(qw(git repack -a -d));
-}
-
-foreach my $git_index (values %index) {
- if ($git_index ne "$git_dir/index") {
- unlink($git_index);
- }
-}
-
-if (defined $orig_git_index) {
- $ENV{GIT_INDEX_FILE} = $orig_git_index;
-} else {
- delete $ENV{GIT_INDEX_FILE};
-}
-
-# Now switch back to the branch we were in before all of this happened
-if ($orig_branch) {
- print "DONE.\n" if $opt_v;
- if ($opt_i) {
- exit 0;
- }
- my $tip_at_end = `git rev-parse --verify HEAD`;
- if ($tip_at_start ne $tip_at_end) {
- for ($tip_at_start, $tip_at_end) { chomp; }
- print "Fetched into the current branch.\n" if $opt_v;
- system(qw(git read-tree -u -m),
- $tip_at_start, $tip_at_end);
- die "Fast-forward update failed: $?\n" if $?;
- }
- else {
- system(qw(git merge cvsimport HEAD), "$remote/$opt_o");
- die "Could not merge $opt_o into the current branch.\n" if $?;
- }
-} else {
- $orig_branch = "master";
- print "DONE; creating $orig_branch branch\n" if $opt_v;
- system("git", "update-ref", "refs/heads/master", "$remote/$opt_o")
- unless defined get_headref('refs/heads/master');
- system("git", "symbolic-ref", "$remote/HEAD", "$remote/$opt_o")
- if ($opt_r && $opt_o ne 'HEAD');
- system('git', 'update-ref', 'HEAD', "$orig_branch");
- unless ($opt_i) {
- system(qw(git checkout -f));
- die "checkout failed: $?\n" if $?;
- }
-}
diff --git a/git-cvsimport.sh b/git-cvsimport.sh
new file mode 100755
index 0000000..71ba11d
--- /dev/null
+++ b/git-cvsimport.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+GIT_CVSPS_VERSION=2
+
+exec git cvsimport-$GIT_CVSPS_VERSION "$@"
--
1.8.1.421.g6236851
^ permalink raw reply related
* [PATCH 3/3] cvsimport: start adding cvsps 3.x support
From: Junio C Hamano @ 2013-01-14 1:40 UTC (permalink / raw)
To: git; +Cc: jrnieder, mhagger, esr, chris
In-Reply-To: <1358127629-7500-1-git-send-email-gitster@pobox.com>
The new cvsps 3.x series lacks support of some options cvsps 2.x
series had and used by cvsimport-2; add a replacement program from
the author of cvsps 3.x and allow users to choose it by setting the
GIT_CVSPS_VERSION environment variable to 3. We would later add
support to auto-detect the version of installed cvsps to this code
when the environment variable is not set.
Note that in this step, cvsimport-3 that relies on cvsps 3.x does
not have any test coverage. As cvsimport-3 supports most of the
command line options cvsimport-2, we should be able to tweak some of
t96xx tests and run them with GIT_CVSPS_VERSION set to both 2 and 3,
but that is a topic of later patches that should come on top.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
Makefile | 18 ++-
git-cvsimport-3.py | 344 +++++++++++++++++++++++++++++++++++++++++++++++++++++
git-cvsimport.sh | 4 +-
3 files changed, 359 insertions(+), 7 deletions(-)
create mode 100755 git-cvsimport-3.py
diff --git a/Makefile b/Makefile
index b022db2..060cdc2 100644
--- a/Makefile
+++ b/Makefile
@@ -470,8 +470,9 @@ SCRIPT_PERL += git-relink.perl
SCRIPT_PERL += git-send-email.perl
SCRIPT_PERL += git-svn.perl
-SCRIPT_PYTHON += git-remote-testpy.py
+SCRIPT_PYTHON += git-cvsimport-3.py
SCRIPT_PYTHON += git-p4.py
+SCRIPT_PYTHON += git-remote-testpy.py
SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
$(patsubst %.perl,%,$(SCRIPT_PERL)) \
@@ -575,8 +576,11 @@ endif
ifndef CVSPS2_PATH
CVSPS2_PATH = cvsps
endif
+ifndef CVSPS3_PATH
+ CVSPS3_PATH = cvsps
+endif
-export PERL_PATH PYTHON_PATH CVSPS2_PATH
+export PERL_PATH PYTHON_PATH CVSPS2_PATH CVSPS3_PATH
LIB_FILE = libgit.a
XDIFF_LIB = xdiff/lib.a
@@ -1515,6 +1519,7 @@ PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
PYTHON_PATH_SQ = $(subst ','\'',$(PYTHON_PATH))
TCLTK_PATH_SQ = $(subst ','\'',$(TCLTK_PATH))
CVSPS2_PATH_SQ = $(subst ','\'',$(CVSPS2_PATH))
+CVSPS3_PATH_SQ = $(subst ','\'',$(CVSPS3_PATH))
DIFF_SQ = $(subst ','\'',$(DIFF))
LIBS = $(GITLIBS) $(EXTLIBS)
@@ -1757,12 +1762,15 @@ ifndef NO_PYTHON
$(patsubst %.py,%,$(SCRIPT_PYTHON)): GIT-CFLAGS GIT-PREFIX GIT-PYTHON-VARS
$(patsubst %.py,%,$(SCRIPT_PYTHON)): % : %.py
$(QUIET_GEN)$(RM) $@ $@+ && \
- INSTLIBDIR=`MAKEFLAGS= $(MAKE) -C git_remote_helpers -s \
+ INSTLIBDIR_SQ=`MAKEFLAGS= $(MAKE) -C git_remote_helpers -s \
--no-print-directory prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' \
- instlibdir` && \
+ instlibdir | \
+ sed -e "s/'/'\''/g"` && \
+ echo "InstLibDir is <$$INSTLIBDIR_SQ>" && \
sed -e '1s|#!.*python|#!$(PYTHON_PATH_SQ)|' \
-e 's|\(os\.getenv("GITPYTHONLIB"\)[^)]*)|\1,"@@INSTLIBDIR@@")|' \
- -e 's|@@INSTLIBDIR@@|'"$$INSTLIBDIR"'|g' \
+ -e 's|@@CVSPS3_PATH@@|$(CVSPS3_PATH_SQ)|g' \
+ -e 's|@@INSTLIBDIR@@|'"$$INSTLIBDIR_SQ"'|g' \
$@.py >$@+ && \
chmod +x $@+ && \
mv $@+ $@
diff --git a/git-cvsimport-3.py b/git-cvsimport-3.py
new file mode 100755
index 0000000..57f13b7
--- /dev/null
+++ b/git-cvsimport-3.py
@@ -0,0 +1,344 @@
+#!/usr/bin/env python
+#
+# Import CVS history into git
+#
+# Intended to be a near-workalike of Matthias Urlichs's Perl implementation.
+#
+# By Eric S. Raymond <esr@thyrsus.com>, December 2012
+# May be redistributed under the license of the git project.
+
+import sys
+
+if sys.hexversion < 0x02060000:
+ sys.stderr.write("git cvsimport: requires Python 2.6 or later.\n")
+ sys.exit(1)
+
+import os, getopt, subprocess, tempfile, shutil
+
+DEBUG_COMMANDS = 1
+
+class Fatal(Exception):
+ "Unrecoverable error."
+ def __init__(self, msg):
+ Exception.__init__(self)
+ self.msg = msg
+
+def do_or_die(dcmd, legend=""):
+ "Either execute a command or raise a fatal exception."
+ if legend:
+ legend = " " + legend
+ if verbose >= DEBUG_COMMANDS:
+ sys.stdout.write("git cvsimport: executing '%s'%s\n" % (dcmd, legend))
+ try:
+ retcode = subprocess.call(dcmd, shell=True)
+ if retcode < 0:
+ raise Fatal("git cvsimport: child was terminated by signal %d." % -retcode)
+ elif retcode != 0:
+ raise Fatal("git cvsimport: child returned %d." % retcode)
+ except (OSError, IOError) as e:
+ raise Fatal("git cvsimport: execution of %s%s failed: %s" % (dcmd, legend, e))
+
+def capture_or_die(dcmd, legend=""):
+ "Either execute a command and capture its output or die."
+ if legend:
+ legend = " " + legend
+ if verbose >= DEBUG_COMMANDS:
+ sys.stdout.write("git cvsimport: executing '%s'%s\n" % (dcmd, legend))
+ try:
+ return subprocess.check_output(dcmd, shell=True)
+ except subprocess.CalledProcessError as e:
+ if e.returncode < 0:
+ sys.stderr.write("git cvsimport: child was terminated by signal %d." % -e.returncode)
+ elif e.returncode != 0:
+ sys.stderr.write("git cvsimport: child returned %d." % e.returncode)
+ sys.exit(1)
+
+class cvsps:
+ "Method class for cvsps back end."
+
+ cvsps = "@@CVSPS3_PATH@@"
+ def __init__(self):
+ self.opts = ""
+ self.revmap = None
+ def set_repo(self, val):
+ "Set the repository root option."
+ if not val.startswith(":"):
+ if not val.startswith(os.sep):
+ val = os.path.abspath(val)
+ val = ":local:" + val
+ self.opts += " --root '%s'" % val
+ def set_authormap(self, val):
+ "Set the author-map file."
+ self.opts += " -A '%s'" % val
+ def set_fuzz(self, val):
+ "Set the commit-similarity window."
+ self.opts += " -z %s" % val
+ def set_nokeywords(self):
+ "Suppress CVS keyword expansion."
+ self.opts += " -k"
+ def add_opts(self, val):
+ "Add options to the engine command line."
+ self.opts += " " + val
+ def set_exclusion(self, val):
+ "Set a file exclusion regexp."
+ self.opts += " -n -f '%s'" % val
+ def set_after(self, val):
+ "Set a date threshold for incremental import."
+ self.opts += " -d '%s'" % val
+ def set_revmap(self, val):
+ "Set the file to which the engine should dump a reference map."
+ self.revmap = val
+ self.opts += " -R '%s'" % self.revmap
+ def set_module(self, val):
+ "Set the module to query."
+ self.opts += " " + val
+ def command(self):
+ "Emit the command implied by all previous options."
+ return self.cvsps + "--fast-export " + self.opts
+
+class cvs2git:
+ "Method class for cvs2git back end."
+ def __init__(self):
+ self.opts = ""
+ self.modulepath = "."
+ def set_authormap(self, _val):
+ "Set the author-map file."
+ sys.stderr.write("git cvsimport: author maping is not supported with cvs2git.\n")
+ sys.exit(1)
+ def set_repo(self, _val):
+ "Set the repository root option."
+ sys.stderr.write("git cvsimport: cvs2git must run within a repository checkout directory.\n")
+ sys.exit(1)
+ def set_fuzz(self, _val):
+ "Set the commit-similarity window."
+ sys.stderr.write("git cvsimport: fuzz setting is not supported with cvs2git.\n")
+ sys.exit(1)
+ def set_nokeywords(self):
+ "Suppress CVS keyword expansion."
+ self.opts += " --keywords-off"
+ def add_opts(self, val):
+ "Add options to the engine command line."
+ self.opts += " " + val
+ def set_exclusion(self, val):
+ "Set a file exclusion regexp."
+ self.opts += " --exclude='%s'" % val
+ def set_after(self, _val):
+ "Set a date threshold for incremental import."
+ sys.stderr.write("git cvsimport: incremental import is not supported with cvs2git.\n")
+ sys.exit(1)
+ def set_revmap(self, _val):
+ "Set the file to which the engine should dump a reference map."
+ sys.stderr.write("git cvsimport: can't get a reference map from cvs2git.\n")
+ sys.exit(1)
+ def set_module(self, val):
+ "Set the module to query."
+ self.modulepath = " " + val
+ def command(self):
+ "Emit the command implied by all previous options."
+ return "(cvs2git --username=git-cvsimport --quiet --quiet --blobfile={0} --dumpfile={1} {2} {3} && cat {0} {1} && rm {0} {1})".format(tempfile.mkstemp()[1], tempfile.mkstemp()[1], self.opts, self.modulepath)
+
+class filesource:
+ "Method class for file-source back end."
+ def __init__(self, filename):
+ self.filename = filename
+ def __complain(self, legend):
+ sys.stderr.write("git cvsimport: %s with file source.\n" % legend)
+ sys.exit(1)
+ def set_repo(self, _val):
+ "Set the repository root option."
+ self.__complain("repository can't be set")
+ def set_authormap(self, _val):
+ "Set the author-map file."
+ sys.stderr.write("git cvsimport: author mapping is not supported with filesource.\n")
+ sys.exit(1)
+ def set_fuzz(self, _val):
+ "Set the commit-similarity window."
+ self.__complain("fuzz can't be set")
+ def set_nokeywords(self, _val):
+ "Suppress CVS keyword expansion."
+ self.__complain("keyword suppression can't be set")
+ def add_opts(self, _val):
+ "Add options to the engine command line."
+ self.__complain("other options can't be set")
+ def set_exclusion(self, _val):
+ "Set a file exclusion regexp."
+ self.__complain("exclusions can't be set")
+ def set_after(self, _val):
+ "Set a date threshold for incremental import."
+ pass
+ def set_revmap(self, _val):
+ "Set the file to which the engine should dump a reference map."
+ sys.stderr.write("git cvsimport: can't get a reference map from cvs2git.\n")
+ sys.exit(1)
+ def set_module(self, _val):
+ "Set the module to query."
+ self.__complain("module can't be set")
+ def command(self):
+ "Emit the command implied by all previous options."
+ return "cat " + self.filename
+
+if __name__ == '__main__':
+ if sys.hexversion < 0x02060000:
+ sys.stderr.write("git cvsimport: requires Python 2.6 or later.\n")
+ sys.exit(1)
+ (options, arguments) = getopt.getopt(sys.argv[1:], "vbe:d:C:r:o:ikus:p:z:P:S:aL:A:Rh")
+ verbose = 0
+ bare = False
+ root = None
+ outdir = os.getcwd()
+ remotize = False
+ import_only = False
+ underscore_to_dot = False
+ slashsubst = None
+ authormap = None
+ revisionmap = False
+ backend = cvsps()
+ for (opt, val) in options:
+ if opt == '-v':
+ verbose += 1
+ elif opt == '-b':
+ bare = True
+ elif opt == '-e':
+ for cls in (cvsps, cvs2git):
+ if cls.__name__ == val:
+ backend = cls()
+ break
+ else:
+ sys.stderr.write("git cvsimport: unknown engine %s.\n" % val)
+ sys.exit(1)
+ elif opt == '-d':
+ backend.set_repo(val)
+ elif opt == '-C':
+ outdir = val
+ elif opt == '-r':
+ remotize = True
+ elif opt == '-o':
+ sys.stderr.write("git cvsimport: -o is no longer supported.\n")
+ sys.exit(1)
+ elif opt == '-i':
+ import_only = True
+ elif opt == '-k':
+ backend.set_nokeywords()
+ elif opt == '-u':
+ underscore_to_dot = True
+ elif opt == '-s':
+ slashsubst = val
+ elif opt == '-p':
+ backend.add_opts(val.replace(",", " "))
+ elif opt == '-z':
+ backend.set_fuzz(val)
+ elif opt == '-P':
+ backend = filesource(val)
+ sys.exit(1)
+ elif opt in ('-m', '-M'):
+ sys.stderr.write("git cvsimport: -m and -M are no longer supported: use reposurgeon instead.\n")
+ sys.exit(1)
+ elif opt == '-S':
+ backend.set_exclusion(val)
+ elif opt == '-a':
+ sys.stderr.write("git cvsimport: -a is no longer supported.\n")
+ sys.exit(1)
+ elif opt == '-L':
+ sys.stderr.write("git cvsimport: -L is no longer supported.\n")
+ sys.exit(1)
+ elif opt == '-A':
+ authormap = os.path.abspath(val)
+ elif opt == '-R':
+ revisionmap = True
+ else:
+ print """\
+git cvsimport [-A <author-conv-file>] [-C <git_repository>] [-b] [-d <CVSROOT>]
+ [-e engine] [-h] [-i] [-k] [-p <options-for-cvsps>] [-P <source-file>]
+ [-r <remote>] [-R] [-s <subst>] [-S <regex>] [-u] [-v] [-z <fuzz>]
+ [<CVS_module>]
+"""
+ def metadata(fn, outdir='.'):
+ if bare:
+ return os.path.join(outdir, fn)
+ else:
+ return os.path.join(outdir, ".git", fn)
+
+ try:
+ if outdir:
+ try:
+ # If the output directory does not exist, create it
+ # and initialize it as a git repository.
+ os.mkdir(outdir)
+ do_or_die("git init --quiet " + outdir)
+ except OSError:
+ # Otherwise, assume user wants incremental import.
+ if not bare and not os.path.exists(os.path.join(outdir, ".git")):
+ raise Fatal("output directory is not a git repository")
+ threshold = capture_or_die("git log -1 --format=%ct").strip()
+ backend.set_after(threshold)
+ if revisionmap:
+ backend.set_revmap(tempfile.mkstemp()[1])
+ markmap = tempfile.mkstemp()[1]
+ if arguments:
+ backend.set_module(arguments[0])
+ gitopts = ""
+ if bare:
+ gitopts += " --bare"
+ if revisionmap:
+ gitopts += " --export-marks='%s'" % markmap
+ if authormap:
+ shutil.copyfile(authormap, metadata("cvs-authors", outdir))
+ if os.path.exists(metadata("cvs-authors", outdir)):
+ backend.set_authormap(metadata("cvs-authors", outdir))
+ do_or_die("%s | (cd %s >/dev/null; git fast-import --quiet %s)" \
+ % (backend.command(), outdir, gitopts))
+ os.chdir(outdir)
+ if underscore_to_dot or slashsubst:
+ tagnames = capture_or_die("git tag -l")
+ for tag in tagnames.split():
+ if tag:
+ changed = tag
+ if underscore_to_dot:
+ changed = changed.replace("_", ".")
+ if slashsubst:
+ changed = changed.replace(os.sep, slashsubst)
+ if changed != tag:
+ do_or_die("git tag -f %s %s >/dev/null" % (tag, changed))
+ if underscore_to_dot or slashsubst or remotize:
+ branchnames = capture_or_die("git branch -l")
+ for branch in branchnames.split():
+ if branch:
+ # Ugh - fragile dependency on branch -l output format
+ branch = branch[2:]
+ changed = branch
+ if underscore_to_dot:
+ changed = changed.replace("_", ".")
+ if slashsubst:
+ changed = changed.replace(os.sep, slashsubst)
+ if remotize:
+ changed = os.path.join("remotes", remotize, branch)
+ if changed != branch:
+ do_or_die("branch --m %s %s >/dev/null" % (branch, changed))
+ if revisionmap:
+ refd = {}
+ for line in open(backend.revmap):
+ if line.startswith("#"):
+ continue
+ (fn, rev, mark) = line.split()
+ refd[(fn, rev)] = mark
+ markd = {}
+ for line in open(markmap):
+ if line.startswith("#"):
+ continue
+ (mark, hashd) = line.split()
+ markd[mark] = hashd
+ with open(metadata("cvs-revisions"), "a") as wfp:
+ for ((fn, rev), val) in refd.items():
+ if val in markd:
+ wfp.write("%s %s %s\n" % (fn, rev, markd[val]))
+ os.remove(markmap)
+ os.remove(backend.revmap)
+ if not import_only and not bare:
+ do_or_die("git checkout -q")
+ except Fatal, err:
+ sys.stderr.write("git_cvsimport: " + err.msg + "\n")
+ sys.exit(1)
+ except KeyboardInterrupt:
+ pass
+
+# end
diff --git a/git-cvsimport.sh b/git-cvsimport.sh
index 71ba11d..52ce112 100755
--- a/git-cvsimport.sh
+++ b/git-cvsimport.sh
@@ -1,5 +1,5 @@
#!/bin/sh
-GIT_CVSPS_VERSION=2
+: ${GIT_CVSPS_VERSION=2}
-exec git cvsimport-$GIT_CVSPS_VERSION "$@"
+exec git "cvsimport-$GIT_CVSPS_VERSION" "$@"
--
1.8.1.421.g6236851
^ permalink raw reply related
* Re: [PATCH 2/3] cvsimport: introduce a version-switch wrapper
From: Junio C Hamano @ 2013-01-14 1:47 UTC (permalink / raw)
To: git; +Cc: jrnieder, mhagger, esr, chris
In-Reply-To: <1358127629-7500-3-git-send-email-gitster@pobox.com>
Junio C Hamano <gitster@pobox.com> writes:
> In preparation to have both old and new cvsimport during the
> transition period, rename the cvsimport script to cvsimport-2
> and introduce a small wrapper that we can later change it to
> allow users to run either frontend (with their corresponding
> cvsps backends).
>
> Signed-off-by: Junio C Hamano <gitster@pobox.com>
> ---
> .gitignore | 1 +
> Makefile | 3 +-
> git-cvsimport-2.perl | 1179 ++++++++++++++++++++++++++++++++++++++++++++++++++
> git-cvsimport.perl | 1179 --------------------------------------------------
Sorry, I should have done "format-patch -M" for this one.
^ permalink raw reply
* Re: [PATCH/RFC 4/7] contrib/subtree: Code cleaning and refactoring
From: 郑文辉(Techlive Zheng) @ 2013-01-14 3:30 UTC (permalink / raw)
To: git; +Cc: apenwarr, greened, Techlive Zheng
In-Reply-To: <1358041958-1998-5-git-send-email-techlivezheng@gmail.com>
2013/1/13 Techlive Zheng <techlivezheng@gmail.com>:
> Mostly prepare for the later tests refactoring.
>
> Signed-off-by: Techlive Zheng <techlivezheng@gmail.com>
I am personally a SP-indenting guy, I did not aware of Git's indenting
policy until now, so it is bad that I replaced all the lines initially
indent using HT with SP in the test file of this commit. I will submit
another set of patches with the right indenting.
^ permalink raw reply
* [PATCH/RFC v2 0/8] contrib/subtree: Reroll to follow Git's whitespace policy
From: Techlive Zheng @ 2013-01-14 3:52 UTC (permalink / raw)
To: git; +Cc: apenwarr, greened, Techlive Zheng
In-Reply-To: <1358041958-1998-1-git-send-email-techlivezheng@gmail.com>
David A. Greene (1):
contrib/subtree: Remove test number comments
Techlive Zheng (7):
contrib/subtree: Fix whitespaces
contrib/subtree: Add vim modeline
contrib/subtree: Ignore testing directory
contrib/subtree: Code cleaning and refactoring
contrib/subtree: Make each test self-contained
contrib/subtree: Use %B for the split commit message
contrib/subtree: Handle '--prefix' argument with a slash appended
contrib/subtree/.gitignore | 5 +-
contrib/subtree/git-subtree.sh | 85 +--
contrib/subtree/git-subtree.txt | 55 +-
contrib/subtree/t/t7900-subtree.sh | 1255 +++++++++++++++++++++++-------------
4 files changed, 905 insertions(+), 495 deletions(-)
--
1.8.1
^ permalink raw reply
* [PATCH/RFC v2 1/8] contrib/subtree: Fix whitespaces
From: Techlive Zheng @ 2013-01-14 3:52 UTC (permalink / raw)
To: git; +Cc: apenwarr, greened, Techlive Zheng
In-Reply-To: <1358135541-10349-1-git-send-email-techlivezheng@gmail.com>
Previous code does not fulfill Git's whitespace policy.
Signed-off-by: Techlive Zheng <techlivezheng@gmail.com>
---
contrib/subtree/git-subtree.sh | 68 ++++----
contrib/subtree/git-subtree.txt | 42 ++---
contrib/subtree/t/t7900-subtree.sh | 314 ++++++++++++++++++-------------------
3 files changed, 212 insertions(+), 212 deletions(-)
diff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh
index 920c664..70f86ea 100755
--- a/contrib/subtree/git-subtree.sh
+++ b/contrib/subtree/git-subtree.sh
@@ -5,7 +5,7 @@
# Copyright (C) 2009 Avery Pennarun <apenwarr@gmail.com>
#
if [ $# -eq 0 ]; then
- set -- -h
+ set -- -h
fi
OPTS_SPEC="\
git subtree add --prefix=<prefix> <commit>
@@ -110,9 +110,9 @@ if [ -z "$prefix" ]; then
fi
case "$command" in
- add) [ -e "$prefix" ] &&
+ add) [ -e "$prefix" ] &&
die "prefix '$prefix' already exists." ;;
- *) [ -e "$prefix" ] ||
+ *) [ -e "$prefix" ] ||
die "'$prefix' does not exist; use 'git subtree add'" ;;
esac
@@ -181,8 +181,8 @@ cache_set()
oldrev="$1"
newrev="$2"
if [ "$oldrev" != "latest_old" \
- -a "$oldrev" != "latest_new" \
- -a -e "$cachedir/$oldrev" ]; then
+ -a "$oldrev" != "latest_new" \
+ -a -e "$cachedir/$oldrev" ]; then
die "cache for $oldrev already exists!"
fi
echo "$newrev" >"$cachedir/$oldrev"
@@ -304,7 +304,7 @@ copy_commit()
read GIT_COMMITTER_NAME
read GIT_COMMITTER_EMAIL
read GIT_COMMITTER_DATE
- export GIT_AUTHOR_NAME \
+ export GIT_AUTHOR_NAME \
GIT_AUTHOR_EMAIL \
GIT_AUTHOR_DATE \
GIT_COMMITTER_NAME \
@@ -327,7 +327,7 @@ add_msg()
fi
cat <<-EOF
$commit_message
-
+
git-subtree-dir: $dir
git-subtree-mainline: $latest_old
git-subtree-split: $latest_new
@@ -355,7 +355,7 @@ rejoin_msg()
fi
cat <<-EOF
$commit_message
-
+
git-subtree-dir: $dir
git-subtree-mainline: $latest_old
git-subtree-split: $latest_new
@@ -368,7 +368,7 @@ squash_msg()
oldsub="$2"
newsub="$3"
newsub_short=$(git rev-parse --short "$newsub")
-
+
if [ -n "$oldsub" ]; then
oldsub_short=$(git rev-parse --short "$oldsub")
echo "Squashed '$dir/' changes from $oldsub_short..$newsub_short"
@@ -378,7 +378,7 @@ squash_msg()
else
echo "Squashed '$dir/' content from commit $newsub_short"
fi
-
+
echo
echo "git-subtree-dir: $dir"
echo "git-subtree-split: $newsub"
@@ -427,7 +427,7 @@ new_squash_commit()
newsub="$3"
tree=$(toptree_for_commit $newsub) || exit $?
if [ -n "$old" ]; then
- squash_msg "$dir" "$oldsub" "$newsub" |
+ squash_msg "$dir" "$oldsub" "$newsub" |
git commit-tree "$tree" -p "$old" || exit $?
else
squash_msg "$dir" "" "$newsub" |
@@ -455,7 +455,7 @@ copy_or_skip()
else
nonidentical="$parent"
fi
-
+
# sometimes both old parents map to the same newparent;
# eliminate duplicates
is_new=1
@@ -470,7 +470,7 @@ copy_or_skip()
p="$p -p $parent"
fi
done
-
+
if [ -n "$identical" ]; then
echo $identical
else
@@ -495,14 +495,14 @@ cmd_add()
fi
ensure_clean
-
+
if [ $# -eq 1 ]; then
"cmd_add_commit" "$@"
elif [ $# -eq 2 ]; then
"cmd_add_repository" "$@"
else
- say "error: parameters were '$@'"
- die "Provide either a refspec or a repository and refspec."
+ say "error: parameters were '$@'"
+ die "Provide either a refspec or a repository and refspec."
fi
}
@@ -522,19 +522,19 @@ cmd_add_commit()
revs=$(git rev-parse $default --revs-only "$@") || exit $?
set -- $revs
rev="$1"
-
+
debug "Adding $dir as '$rev'..."
git read-tree --prefix="$dir" $rev || exit $?
git checkout -- "$dir" || exit $?
tree=$(git write-tree) || exit $?
-
+
headrev=$(git rev-parse HEAD) || exit $?
if [ -n "$headrev" -a "$headrev" != "$rev" ]; then
headp="-p $headrev"
else
headp=
fi
-
+
if [ -n "$squash" ]; then
rev=$(new_squash_commit "" "" "$rev") || exit $?
commit=$(add_squashed_msg "$rev" "$dir" |
@@ -544,7 +544,7 @@ cmd_add_commit()
git commit-tree $tree $headp -p "$rev") || exit $?
fi
git reset "$commit" || exit $?
-
+
say "Added dir '$dir'"
}
@@ -552,7 +552,7 @@ cmd_split()
{
debug "Splitting $dir..."
cache_setup || exit $?
-
+
if [ -n "$onto" ]; then
debug "Reading history for --onto=$onto..."
git rev-list $onto |
@@ -563,13 +563,13 @@ cmd_split()
cache_set $rev $rev
done
fi
-
+
if [ -n "$ignore_joins" ]; then
unrevs=
else
unrevs="$(find_existing_splits "$dir" "$revs")"
fi
-
+
# We can't restrict rev-list to only $dir here, because some of our
# parents have the $dir contents the root, and those won't match.
# (and rev-list --follow doesn't seem to solve this)
@@ -591,12 +591,12 @@ cmd_split()
debug " parents: $parents"
newparents=$(cache_get $parents)
debug " newparents: $newparents"
-
+
tree=$(subtree_for_commit $rev "$dir")
debug " tree is: $tree"
check_parents $parents
-
+
# ugly. is there no better way to tell if this is a subtree
# vs. a mainline commit? Does it matter?
if [ -z $tree ]; then
@@ -617,7 +617,7 @@ cmd_split()
if [ -z "$latest_new" ]; then
die "No new revisions were found"
fi
-
+
if [ -n "$rejoin" ]; then
debug "Merging split branch into HEAD..."
latest_old=$(cache_get latest_old)
@@ -645,13 +645,13 @@ cmd_merge()
{
revs=$(git rev-parse $default --revs-only "$@") || exit $?
ensure_clean
-
+
set -- $revs
if [ $# -ne 1 ]; then
die "You must provide exactly one revision. Got: '$revs'"
fi
rev="$1"
-
+
if [ -n "$squash" ]; then
first_split="$(find_latest_squash "$dir")"
if [ -z "$first_split" ]; then
@@ -697,15 +697,15 @@ cmd_pull()
cmd_push()
{
if [ $# -ne 2 ]; then
- die "You must provide <repository> <refspec>"
+ die "You must provide <repository> <refspec>"
fi
if [ -e "$dir" ]; then
- repository=$1
- refspec=$2
- echo "git push using: " $repository $refspec
- git push $repository $(git subtree split --prefix=$prefix):refs/heads/$refspec
+ repository=$1
+ refspec=$2
+ echo "git push using: " $repository $refspec
+ git push $repository $(git subtree split --prefix=$prefix):refs/heads/$refspec
else
- die "'$dir' must already exist. Try 'git subtree add'."
+ die "'$dir' must already exist. Try 'git subtree add'."
fi
}
diff --git a/contrib/subtree/git-subtree.txt b/contrib/subtree/git-subtree.txt
index c5bce41..72be8e4 100644
--- a/contrib/subtree/git-subtree.txt
+++ b/contrib/subtree/git-subtree.txt
@@ -86,15 +86,15 @@ merge::
go back in time from v2.5 to v2.4, for example. If your
merge introduces a conflict, you can resolve it in the
usual ways.
-
+
pull::
Exactly like 'merge', but parallels 'git pull' in that
it fetches the given commit from the specified remote
repository.
-
+
push::
Does a 'split' (see below) using the <prefix> supplied
- and then does a 'git push' to push the result to the
+ and then does a 'git push' to push the result to the
repository and refspec. This can be used to push your
subtree to different branches of the remote repository.
@@ -106,19 +106,19 @@ split::
contents of <prefix> at the root of the project instead
of in a subdirectory. Thus, the newly created history
is suitable for export as a separate git repository.
-
+
After splitting successfully, a single commit id is
printed to stdout. This corresponds to the HEAD of the
newly created tree, which you can manipulate however you
want.
-
+
Repeated splits of exactly the same history are
guaranteed to be identical (ie. to produce the same
commit ids). Because of this, if you add new commits
and then re-split, the new commits will be attached as
commits on top of the history you generated last time,
so 'git merge' and friends will work as expected.
-
+
Note that if you use '--squash' when you merge, you
should usually not just '--rejoin' when you split.
@@ -155,26 +155,26 @@ OPTIONS FOR add, merge, push, pull
project, produce only a single commit that contains all
the differences you want to merge, and then merge that
new commit into your project.
-
+
Using this option helps to reduce log clutter. People
rarely want to see every change that happened between
v1.0 and v1.1 of the library they're using, since none of the
interim versions were ever included in their application.
-
+
Using '--squash' also helps avoid problems when the same
subproject is included multiple times in the same
project, or is removed and then re-added. In such a
case, it doesn't make sense to combine the histories
anyway, since it's unclear which part of the history
belongs to which subtree.
-
+
Furthermore, with '--squash', you can switch back and
forth between different versions of a subtree, rather
than strictly forward. 'git subtree merge --squash'
always adjusts the subtree to match the exactly
specified commit, even if getting to that commit would
require undoing some changes that were added earlier.
-
+
Whether or not you use '--squash', changes made in your
local repository remain intact and can be later split
and send upstream to the subproject.
@@ -190,11 +190,11 @@ OPTIONS FOR split
commits with the same commit message, but possibly
different content, from the original commits, this can help
to differentiate them and avoid confusion.
-
+
Whenever you split, you need to use the same
<annotation>, or else you don't have a guarantee that
the new re-created history will be identical to the old
- one. That will prevent merging from working correctly.
+ one. That will prevent merging from working correctly.
git subtree tries to make it work anyway, particularly
if you use --rejoin, but it may not always be effective.
@@ -203,8 +203,8 @@ OPTIONS FOR split
This option is only valid for the split command.
After generating the synthetic history, create a new
- branch called <branch> that contains the new history.
- This is suitable for immediate pushing upstream.
+ branch called <branch> that contains the new history.
+ This is suitable for immediate pushing upstream.
<branch> must not already exist.
--ignore-joins::
@@ -227,7 +227,7 @@ OPTIONS FOR split
revision of the subproject's history that was imported
into your project, and git subtree will attempt to build
its history from there.
-
+
If you used 'git subtree add', you should never need
this option.
@@ -238,18 +238,18 @@ OPTIONS FOR split
history back into your main project. That way, future
splits can search only the part of history that has
been added since the most recent --rejoin.
-
+
If your split commits end up merged into the upstream
subproject, and then you want to get the latest upstream
version, this will allow git's merge algorithm to more
intelligently avoid conflicts (since it knows these
synthetic commits are already part of the upstream
repository).
-
+
Unfortunately, using this option results in 'git log'
showing an extra copy of every new commit that was
created (the original, and the synthetic one).
-
+
If you do all your merges with '--squash', don't use
'--rejoin' when you split, because you don't want the
subproject's history to be part of your project anyway.
@@ -284,7 +284,7 @@ First, get your own copy of the git.git repository:
$ cd test-git
gitweb (commit 1130ef3) was merged into git as of commit
-0a8f4f0, after which it was no longer maintained separately.
+0a8f4f0, after which it was no longer maintained separately.
But imagine it had been maintained separately, and we wanted to
extract git's changes to gitweb since that time, to share with
the upstream. You could do this:
@@ -294,7 +294,7 @@ the upstream. You could do this:
--branch gitweb-latest
$ gitk gitweb-latest
$ git push git@github.com:whatever/gitweb.git gitweb-latest:master
-
+
(We use '0a8f4f0^..' because that means "all the changes from
0a8f4f0 to the current version, including 0a8f4f0 itself.")
@@ -328,7 +328,7 @@ And fast forward again:
$ git subtree merge --prefix=gitweb --squash gitweb-latest
And notice that your change is still intact:
-
+
$ ls -l gitweb/myfile
And you can split it out and look at your changes versus
diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh
index bc2eeb0..b98f7d0 100755
--- a/contrib/subtree/t/t7900-subtree.sh
+++ b/contrib/subtree/t/t7900-subtree.sh
@@ -23,7 +23,7 @@ check_equal()
{
test_debug 'echo'
test_debug "echo \"check a:\" \"{$1}\""
- test_debug "echo \" b:\" \"{$2}\""
+ test_debug "echo \" b:\" \"{$2}\""
if [ "$1" = "$2" ]; then
return 0
else
@@ -62,7 +62,7 @@ last_commit_message()
# 1
test_expect_success 'init subproj' '
- test_create_repo subproj
+ test_create_repo subproj
'
# To the subproject!
@@ -70,24 +70,24 @@ cd subproj
# 2
test_expect_success 'add sub1' '
- create sub1 &&
- git commit -m "sub1" &&
- git branch sub1 &&
- git branch -m master subproj
+ create sub1 &&
+ git commit -m "sub1" &&
+ git branch sub1 &&
+ git branch -m master subproj
'
# 3
test_expect_success 'add sub2' '
- create sub2 &&
- git commit -m "sub2" &&
- git branch sub2
+ create sub2 &&
+ git commit -m "sub2" &&
+ git branch sub2
'
# 4
test_expect_success 'add sub3' '
- create sub3 &&
- git commit -m "sub3" &&
- git branch sub3
+ create sub3 &&
+ git commit -m "sub3" &&
+ git branch sub3
'
# Back to mainline
@@ -95,173 +95,173 @@ cd ..
# 5
test_expect_success 'add main4' '
- create main4 &&
- git commit -m "main4" &&
- git branch -m master mainline &&
- git branch subdir
+ create main4 &&
+ git commit -m "main4" &&
+ git branch -m master mainline &&
+ git branch subdir
'
# 6
test_expect_success 'fetch subproj history' '
- git fetch ./subproj sub1 &&
- git branch sub1 FETCH_HEAD
+ git fetch ./subproj sub1 &&
+ git branch sub1 FETCH_HEAD
'
# 7
test_expect_success 'no subtree exists in main tree' '
- test_must_fail git subtree merge --prefix=subdir sub1
+ test_must_fail git subtree merge --prefix=subdir sub1
'
# 8
test_expect_success 'no pull from non-existant subtree' '
- test_must_fail git subtree pull --prefix=subdir ./subproj sub1
+ test_must_fail git subtree pull --prefix=subdir ./subproj sub1
'
# 9
test_expect_success 'check if --message works for add' '
- git subtree add --prefix=subdir --message="Added subproject" sub1 &&
- check_equal ''"$(last_commit_message)"'' "Added subproject" &&
- undo
+ git subtree add --prefix=subdir --message="Added subproject" sub1 &&
+ check_equal ''"$(last_commit_message)"'' "Added subproject" &&
+ undo
'
# 10
test_expect_success 'check if --message works as -m and --prefix as -P' '
- git subtree add -P subdir -m "Added subproject using git subtree" sub1 &&
- check_equal ''"$(last_commit_message)"'' "Added subproject using git subtree" &&
- undo
+ git subtree add -P subdir -m "Added subproject using git subtree" sub1 &&
+ check_equal ''"$(last_commit_message)"'' "Added subproject using git subtree" &&
+ undo
'
# 11
test_expect_success 'check if --message works with squash too' '
- git subtree add -P subdir -m "Added subproject with squash" --squash sub1 &&
- check_equal ''"$(last_commit_message)"'' "Added subproject with squash" &&
- undo
+ git subtree add -P subdir -m "Added subproject with squash" --squash sub1 &&
+ check_equal ''"$(last_commit_message)"'' "Added subproject with squash" &&
+ undo
'
# 12
test_expect_success 'add subproj to mainline' '
- git subtree add --prefix=subdir/ FETCH_HEAD &&
- check_equal ''"$(last_commit_message)"'' "Add '"'subdir/'"' from commit '"'"'''"$(git rev-parse sub1)"'''"'"'"
+ git subtree add --prefix=subdir/ FETCH_HEAD &&
+ check_equal ''"$(last_commit_message)"'' "Add '"'subdir/'"' from commit '"'"'''"$(git rev-parse sub1)"'''"'"'"
'
# 13
# this shouldn't actually do anything, since FETCH_HEAD is already a parent
test_expect_success 'merge fetched subproj' '
- git merge -m "merge -s -ours" -s ours FETCH_HEAD
+ git merge -m "merge -s -ours" -s ours FETCH_HEAD
'
# 14
test_expect_success 'add main-sub5' '
- create subdir/main-sub5 &&
- git commit -m "main-sub5"
+ create subdir/main-sub5 &&
+ git commit -m "main-sub5"
'
# 15
test_expect_success 'add main6' '
- create main6 &&
- git commit -m "main6 boring"
+ create main6 &&
+ git commit -m "main6 boring"
'
# 16
test_expect_success 'add main-sub7' '
- create subdir/main-sub7 &&
- git commit -m "main-sub7"
+ create subdir/main-sub7 &&
+ git commit -m "main-sub7"
'
# 17
test_expect_success 'fetch new subproj history' '
- git fetch ./subproj sub2 &&
- git branch sub2 FETCH_HEAD
+ git fetch ./subproj sub2 &&
+ git branch sub2 FETCH_HEAD
'
# 18
test_expect_success 'check if --message works for merge' '
- git subtree merge --prefix=subdir -m "Merged changes from subproject" sub2 &&
- check_equal ''"$(last_commit_message)"'' "Merged changes from subproject" &&
- undo
+ git subtree merge --prefix=subdir -m "Merged changes from subproject" sub2 &&
+ check_equal ''"$(last_commit_message)"'' "Merged changes from subproject" &&
+ undo
'
# 19
test_expect_success 'check if --message for merge works with squash too' '
- git subtree merge --prefix subdir -m "Merged changes from subproject using squash" --squash sub2 &&
- check_equal ''"$(last_commit_message)"'' "Merged changes from subproject using squash" &&
- undo
+ git subtree merge --prefix subdir -m "Merged changes from subproject using squash" --squash sub2 &&
+ check_equal ''"$(last_commit_message)"'' "Merged changes from subproject using squash" &&
+ undo
'
# 20
test_expect_success 'merge new subproj history into subdir' '
- git subtree merge --prefix=subdir FETCH_HEAD &&
- git branch pre-split &&
- check_equal ''"$(last_commit_message)"'' "Merge commit '"'"'"$(git rev-parse sub2)"'"'"' into mainline"
+ git subtree merge --prefix=subdir FETCH_HEAD &&
+ git branch pre-split &&
+ check_equal ''"$(last_commit_message)"'' "Merge commit '"'"'"$(git rev-parse sub2)"'"'"' into mainline"
'
# 21
test_expect_success 'Check that prefix argument is required for split' '
- echo "You must provide the --prefix option." > expected &&
- test_must_fail git subtree split > actual 2>&1 &&
- test_debug "echo -n expected: " &&
- test_debug "cat expected" &&
- test_debug "echo -n actual: " &&
- test_debug "cat actual" &&
- test_cmp expected actual &&
- rm -f expected actual
+ echo "You must provide the --prefix option." > expected &&
+ test_must_fail git subtree split > actual 2>&1 &&
+ test_debug "echo -n expected: " &&
+ test_debug "cat expected" &&
+ test_debug "echo -n actual: " &&
+ test_debug "cat actual" &&
+ test_cmp expected actual &&
+ rm -f expected actual
'
# 22
test_expect_success 'Check that the <prefix> exists for a split' '
- echo "'"'"'non-existent-directory'"'"'" does not exist\; use "'"'"'git subtree add'"'"'" > expected &&
- test_must_fail git subtree split --prefix=non-existent-directory > actual 2>&1 &&
- test_debug "echo -n expected: " &&
- test_debug "cat expected" &&
- test_debug "echo -n actual: " &&
- test_debug "cat actual" &&
- test_cmp expected actual
-# rm -f expected actual
+ echo "'"'"'non-existent-directory'"'"'" does not exist\; use "'"'"'git subtree add'"'"'" > expected &&
+ test_must_fail git subtree split --prefix=non-existent-directory > actual 2>&1 &&
+ test_debug "echo -n expected: " &&
+ test_debug "cat expected" &&
+ test_debug "echo -n actual: " &&
+ test_debug "cat actual" &&
+ test_cmp expected actual
+# rm -f expected actual
'
# 23
test_expect_success 'check if --message works for split+rejoin' '
- spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
- git branch spl1 "$spl1" &&
- check_equal ''"$(last_commit_message)"'' "Split & rejoin" &&
- undo
+ spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
+ git branch spl1 "$spl1" &&
+ check_equal ''"$(last_commit_message)"'' "Split & rejoin" &&
+ undo
'
# 24
test_expect_success 'check split with --branch' '
- spl1=$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin) &&
- undo &&
- git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --branch splitbr1 &&
- check_equal ''"$(git rev-parse splitbr1)"'' "$spl1"
+ spl1=$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin) &&
+ undo &&
+ git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --branch splitbr1 &&
+ check_equal ''"$(git rev-parse splitbr1)"'' "$spl1"
'
# 25
test_expect_success 'check split with --branch for an existing branch' '
- spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
- undo &&
- git branch splitbr2 sub1 &&
- git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --branch splitbr2 &&
- check_equal ''"$(git rev-parse splitbr2)"'' "$spl1"
+ spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
+ undo &&
+ git branch splitbr2 sub1 &&
+ git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --branch splitbr2 &&
+ check_equal ''"$(git rev-parse splitbr2)"'' "$spl1"
'
# 26
test_expect_success 'check split with --branch for an incompatible branch' '
- test_must_fail git subtree split --prefix subdir --onto FETCH_HEAD --branch subdir
+ test_must_fail git subtree split --prefix subdir --onto FETCH_HEAD --branch subdir
'
# 27
test_expect_success 'check split+rejoin' '
- spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
- undo &&
- git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --rejoin &&
- check_equal ''"$(last_commit_message)"'' "Split '"'"'subdir/'"'"' into commit '"'"'"$spl1"'"'"'"
+ spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
+ undo &&
+ git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --rejoin &&
+ check_equal ''"$(last_commit_message)"'' "Split '"'"'subdir/'"'"' into commit '"'"'"$spl1"'"'"'"
'
# 28
test_expect_success 'add main-sub8' '
- create subdir/main-sub8 &&
- git commit -m "main-sub8"
+ create subdir/main-sub8 &&
+ git commit -m "main-sub8"
'
# To the subproject!
@@ -269,15 +269,15 @@ cd ./subproj
# 29
test_expect_success 'merge split into subproj' '
- git fetch .. spl1 &&
- git branch spl1 FETCH_HEAD &&
- git merge FETCH_HEAD
+ git fetch .. spl1 &&
+ git branch spl1 FETCH_HEAD &&
+ git merge FETCH_HEAD
'
# 30
test_expect_success 'add sub9' '
- create sub9 &&
- git commit -m "sub9"
+ create sub9 &&
+ git commit -m "sub9"
'
# Back to mainline
@@ -285,20 +285,20 @@ cd ..
# 31
test_expect_success 'split for sub8' '
- split2=''"$(git subtree split --annotate='"'*'"' --prefix subdir/ --rejoin)"''
- git branch split2 "$split2"
+ split2=''"$(git subtree split --annotate='"'*'"' --prefix subdir/ --rejoin)"''
+ git branch split2 "$split2"
'
# 32
test_expect_success 'add main-sub10' '
- create subdir/main-sub10 &&
- git commit -m "main-sub10"
+ create subdir/main-sub10 &&
+ git commit -m "main-sub10"
'
# 33
test_expect_success 'split for sub10' '
- spl3=''"$(git subtree split --annotate='"'*'"' --prefix subdir --rejoin)"'' &&
- git branch spl3 "$spl3"
+ spl3=''"$(git subtree split --annotate='"'*'"' --prefix subdir --rejoin)"'' &&
+ git branch spl3 "$spl3"
'
# To the subproject!
@@ -306,10 +306,10 @@ cd ./subproj
# 34
test_expect_success 'merge split into subproj' '
- git fetch .. spl3 &&
- git branch spl3 FETCH_HEAD &&
- git merge FETCH_HEAD &&
- git branch subproj-merge-spl3
+ git fetch .. spl3 &&
+ git branch spl3 FETCH_HEAD &&
+ git merge FETCH_HEAD &&
+ git branch subproj-merge-spl3
'
chkm="main4 main6"
@@ -320,14 +320,14 @@ chks_sub=$(echo $chks | multiline | sed 's,^,subdir/,' | fixnl)
# 35
test_expect_success 'make sure exactly the right set of files ends up in the subproj' '
- subfiles=''"$(git ls-files | fixnl)"'' &&
- check_equal "$subfiles" "$chkms $chks"
+ subfiles=''"$(git ls-files | fixnl)"'' &&
+ check_equal "$subfiles" "$chkms $chks"
'
# 36
test_expect_success 'make sure the subproj history *only* contains commits that affect the subdir' '
- allchanges=''"$(git log --name-only --pretty=format:'"''"' | sort | fixnl)"'' &&
- check_equal "$allchanges" "$chkms $chks"
+ allchanges=''"$(git log --name-only --pretty=format:'"''"' | sort | fixnl)"'' &&
+ check_equal "$allchanges" "$chkms $chks"
'
# Back to mainline
@@ -335,35 +335,35 @@ cd ..
# 37
test_expect_success 'pull from subproj' '
- git fetch ./subproj subproj-merge-spl3 &&
- git branch subproj-merge-spl3 FETCH_HEAD &&
- git subtree pull --prefix=subdir ./subproj subproj-merge-spl3
+ git fetch ./subproj subproj-merge-spl3 &&
+ git branch subproj-merge-spl3 FETCH_HEAD &&
+ git subtree pull --prefix=subdir ./subproj subproj-merge-spl3
'
# 38
test_expect_success 'make sure exactly the right set of files ends up in the mainline' '
- mainfiles=''"$(git ls-files | fixnl)"'' &&
- check_equal "$mainfiles" "$chkm $chkms_sub $chks_sub"
+ mainfiles=''"$(git ls-files | fixnl)"'' &&
+ check_equal "$mainfiles" "$chkm $chkms_sub $chks_sub"
'
# 39
test_expect_success 'make sure each filename changed exactly once in the entire history' '
- # main-sub?? and /subdir/main-sub?? both change, because those are the
- # changes that were split into their own history. And subdir/sub?? never
- # change, since they were *only* changed in the subtree branch.
- allchanges=''"$(git log --name-only --pretty=format:'"''"' | sort | fixnl)"'' &&
- check_equal "$allchanges" ''"$(echo $chkms $chkm $chks $chkms_sub | multiline | sort | fixnl)"''
+ # main-sub?? and /subdir/main-sub?? both change, because those are the
+ # changes that were split into their own history. And subdir/sub?? never
+ # change, since they were *only* changed in the subtree branch.
+ allchanges=''"$(git log --name-only --pretty=format:'"''"' | sort | fixnl)"'' &&
+ check_equal "$allchanges" ''"$(echo $chkms $chkm $chks $chkms_sub | multiline | sort | fixnl)"''
'
# 40
test_expect_success 'make sure the --rejoin commits never make it into subproj' '
- check_equal ''"$(git log --pretty=format:'"'%s'"' HEAD^2 | grep -i split)"'' ""
+ check_equal ''"$(git log --pretty=format:'"'%s'"' HEAD^2 | grep -i split)"'' ""
'
# 41
test_expect_success 'make sure no "git subtree" tagged commits make it into subproj' '
- # They are meaningless to subproj since one side of the merge refers to the mainline
- check_equal ''"$(git log --pretty=format:'"'%s%n%b'"' HEAD^2 | grep "git-subtree.*:")"'' ""
+ # They are meaningless to subproj since one side of the merge refers to the mainline
+ check_equal ''"$(git log --pretty=format:'"'%s%n%b'"' HEAD^2 | grep "git-subtree.*:")"'' ""
'
# prepare second pair of repositories
@@ -372,30 +372,30 @@ cd test2
# 42
test_expect_success 'init main' '
- test_create_repo main
+ test_create_repo main
'
cd main
# 43
test_expect_success 'add main1' '
- create main1 &&
- git commit -m "main1"
+ create main1 &&
+ git commit -m "main1"
'
cd ..
# 44
test_expect_success 'init sub' '
- test_create_repo sub
+ test_create_repo sub
'
cd sub
# 45
test_expect_success 'add sub2' '
- create sub2 &&
- git commit -m "sub2"
+ create sub2 &&
+ git commit -m "sub2"
'
cd ../main
@@ -404,37 +404,37 @@ cd ../main
# 46
test_expect_success 'add sub as subdir in main' '
- git fetch ../sub master &&
- git branch sub2 FETCH_HEAD &&
- git subtree add --prefix subdir sub2
+ git fetch ../sub master &&
+ git branch sub2 FETCH_HEAD &&
+ git subtree add --prefix subdir sub2
'
cd ../sub
# 47
test_expect_success 'add sub3' '
- create sub3 &&
- git commit -m "sub3"
+ create sub3 &&
+ git commit -m "sub3"
'
cd ../main
# 48
test_expect_success 'merge from sub' '
- git fetch ../sub master &&
- git branch sub3 FETCH_HEAD &&
- git subtree merge --prefix subdir sub3
+ git fetch ../sub master &&
+ git branch sub3 FETCH_HEAD &&
+ git subtree merge --prefix subdir sub3
'
# 49
test_expect_success 'add main-sub4' '
- create subdir/main-sub4 &&
- git commit -m "main-sub4"
+ create subdir/main-sub4 &&
+ git commit -m "main-sub4"
'
# 50
test_expect_success 'split for main-sub4 without --onto' '
- git subtree split --prefix subdir --branch mainsub4
+ git subtree split --prefix subdir --branch mainsub4
'
# at this point, the new commit parent should be sub3 if it is not,
@@ -444,23 +444,23 @@ test_expect_success 'split for main-sub4 without --onto' '
# 51
test_expect_success 'check that the commit parent is sub3' '
- check_equal ''"$(git log --pretty=format:%P -1 mainsub4)"'' ''"$(git rev-parse sub3)"''
+ check_equal ''"$(git log --pretty=format:%P -1 mainsub4)"'' ''"$(git rev-parse sub3)"''
'
# 52
test_expect_success 'add main-sub5' '
- mkdir subdir2 &&
- create subdir2/main-sub5 &&
- git commit -m "main-sub5"
+ mkdir subdir2 &&
+ create subdir2/main-sub5 &&
+ git commit -m "main-sub5"
'
# 53
test_expect_success 'split for main-sub5 without --onto' '
- # also test that we still can split out an entirely new subtree
- # if the parent of the first commit in the tree is not empty,
- # then the new subtree has accidently been attached to something
- git subtree split --prefix subdir2 --branch mainsub5 &&
- check_equal ''"$(git log --pretty=format:%P -1 mainsub5)"'' ""
+ # also test that we still can split out an entirely new subtree
+ # if the parent of the first commit in the tree is not empty,
+ # then the new subtree has accidently been attached to something
+ git subtree split --prefix subdir2 --branch mainsub5 &&
+ check_equal ''"$(git log --pretty=format:%P -1 mainsub5)"'' ""
'
# make sure no patch changes more than one file. The original set of commits
@@ -489,20 +489,20 @@ joincommits()
# 54
test_expect_success 'verify one file change per commit' '
- x= &&
- list=''"$(git log --pretty=format:'"'commit: %H'"' | joincommits)"'' &&
-# test_debug "echo HERE" &&
-# test_debug "echo ''"$list"''" &&
- (git log --pretty=format:'"'commit: %H'"' | joincommits |
- ( while read commit a b; do
- test_debug "echo Verifying commit "''"$commit"''
- test_debug "echo a: "''"$a"''
- test_debug "echo b: "''"$b"''
- check_equal "$b" ""
- x=1
- done
- check_equal "$x" 1
- ))
+ x= &&
+ list=''"$(git log --pretty=format:'"'commit: %H'"' | joincommits)"'' &&
+# test_debug "echo HERE" &&
+# test_debug "echo ''"$list"''" &&
+ (git log --pretty=format:'"'commit: %H'"' | joincommits |
+ ( while read commit a b; do
+ test_debug "echo Verifying commit "''"$commit"''
+ test_debug "echo a: "''"$a"''
+ test_debug "echo b: "''"$b"''
+ check_equal "$b" ""
+ x=1
+ done
+ check_equal "$x" 1
+ ))
'
test_done
--
1.8.1
^ permalink raw reply related
* [PATCH/RFC v2 2/8] contrib/subtree: Add vim modeline
From: Techlive Zheng @ 2013-01-14 3:52 UTC (permalink / raw)
To: git; +Cc: apenwarr, greened, Techlive Zheng
In-Reply-To: <1358135541-10349-1-git-send-email-techlivezheng@gmail.com>
Signed-off-by: Techlive Zheng <techlivezheng@gmail.com>
---
contrib/subtree/git-subtree.sh | 2 ++
contrib/subtree/t/t7900-subtree.sh | 2 ++
2 files changed, 4 insertions(+)
diff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh
index 70f86ea..88903c0 100755
--- a/contrib/subtree/git-subtree.sh
+++ b/contrib/subtree/git-subtree.sh
@@ -710,3 +710,5 @@ cmd_push()
}
"cmd_$command" "$@"
+
+# vim: set ts=4 sw=4 noet
diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh
index b98f7d0..e32d31a 100755
--- a/contrib/subtree/t/t7900-subtree.sh
+++ b/contrib/subtree/t/t7900-subtree.sh
@@ -506,3 +506,5 @@ test_expect_success 'verify one file change per commit' '
'
test_done
+
+# vim: set ts=4 sw=4 noet
--
1.8.1
^ permalink raw reply related
* [PATCH/RFC v2 3/8] contrib/subtree: Ignore testing directory
From: Techlive Zheng @ 2013-01-14 3:52 UTC (permalink / raw)
To: git; +Cc: apenwarr, greened, Techlive Zheng
In-Reply-To: <1358135541-10349-1-git-send-email-techlivezheng@gmail.com>
Signed-off-by: Techlive Zheng <techlivezheng@gmail.com>
---
contrib/subtree/.gitignore | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/contrib/subtree/.gitignore b/contrib/subtree/.gitignore
index 91360a3..59aeeb4 100644
--- a/contrib/subtree/.gitignore
+++ b/contrib/subtree/.gitignore
@@ -1,6 +1,5 @@
*~
git-subtree
-git-subtree.xml
git-subtree.1
-mainline
-subproj
+git-subtree.xml
+t/trash\ directory.*
--
1.8.1
^ permalink raw reply related
* [PATCH/RFC v2 4/8] contrib/subtree: Remove test number comments
From: Techlive Zheng @ 2013-01-14 3:52 UTC (permalink / raw)
To: git; +Cc: apenwarr, greened, Techlive Zheng
In-Reply-To: <1358135541-10349-1-git-send-email-techlivezheng@gmail.com>
From: "David A. Greene" <greened@obbligato.org>
Delete the comments indicating test numbers as it causes maintenance
headaches. t*.sh -i will help us find any broken tests.
Signed-off-by: David A. Greene <greened@obbligato.org>
Signed-off-by: Techlive Zheng <techlivezheng@gmail.com>
---
contrib/subtree/t/t7900-subtree.sh | 55 --------------------------------------
1 file changed, 55 deletions(-)
diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh
index e32d31a..851d00c 100755
--- a/contrib/subtree/t/t7900-subtree.sh
+++ b/contrib/subtree/t/t7900-subtree.sh
@@ -60,7 +60,6 @@ last_commit_message()
git log --pretty=format:%s -1
}
-# 1
test_expect_success 'init subproj' '
test_create_repo subproj
'
@@ -68,7 +67,6 @@ test_expect_success 'init subproj' '
# To the subproject!
cd subproj
-# 2
test_expect_success 'add sub1' '
create sub1 &&
git commit -m "sub1" &&
@@ -76,14 +74,12 @@ test_expect_success 'add sub1' '
git branch -m master subproj
'
-# 3
test_expect_success 'add sub2' '
create sub2 &&
git commit -m "sub2" &&
git branch sub2
'
-# 4
test_expect_success 'add sub3' '
create sub3 &&
git commit -m "sub3" &&
@@ -93,7 +89,6 @@ test_expect_success 'add sub3' '
# Back to mainline
cd ..
-# 5
test_expect_success 'add main4' '
create main4 &&
git commit -m "main4" &&
@@ -101,101 +96,85 @@ test_expect_success 'add main4' '
git branch subdir
'
-# 6
test_expect_success 'fetch subproj history' '
git fetch ./subproj sub1 &&
git branch sub1 FETCH_HEAD
'
-# 7
test_expect_success 'no subtree exists in main tree' '
test_must_fail git subtree merge --prefix=subdir sub1
'
-# 8
test_expect_success 'no pull from non-existant subtree' '
test_must_fail git subtree pull --prefix=subdir ./subproj sub1
'
-# 9
test_expect_success 'check if --message works for add' '
git subtree add --prefix=subdir --message="Added subproject" sub1 &&
check_equal ''"$(last_commit_message)"'' "Added subproject" &&
undo
'
-# 10
test_expect_success 'check if --message works as -m and --prefix as -P' '
git subtree add -P subdir -m "Added subproject using git subtree" sub1 &&
check_equal ''"$(last_commit_message)"'' "Added subproject using git subtree" &&
undo
'
-# 11
test_expect_success 'check if --message works with squash too' '
git subtree add -P subdir -m "Added subproject with squash" --squash sub1 &&
check_equal ''"$(last_commit_message)"'' "Added subproject with squash" &&
undo
'
-# 12
test_expect_success 'add subproj to mainline' '
git subtree add --prefix=subdir/ FETCH_HEAD &&
check_equal ''"$(last_commit_message)"'' "Add '"'subdir/'"' from commit '"'"'''"$(git rev-parse sub1)"'''"'"'"
'
-# 13
# this shouldn't actually do anything, since FETCH_HEAD is already a parent
test_expect_success 'merge fetched subproj' '
git merge -m "merge -s -ours" -s ours FETCH_HEAD
'
-# 14
test_expect_success 'add main-sub5' '
create subdir/main-sub5 &&
git commit -m "main-sub5"
'
-# 15
test_expect_success 'add main6' '
create main6 &&
git commit -m "main6 boring"
'
-# 16
test_expect_success 'add main-sub7' '
create subdir/main-sub7 &&
git commit -m "main-sub7"
'
-# 17
test_expect_success 'fetch new subproj history' '
git fetch ./subproj sub2 &&
git branch sub2 FETCH_HEAD
'
-# 18
test_expect_success 'check if --message works for merge' '
git subtree merge --prefix=subdir -m "Merged changes from subproject" sub2 &&
check_equal ''"$(last_commit_message)"'' "Merged changes from subproject" &&
undo
'
-# 19
test_expect_success 'check if --message for merge works with squash too' '
git subtree merge --prefix subdir -m "Merged changes from subproject using squash" --squash sub2 &&
check_equal ''"$(last_commit_message)"'' "Merged changes from subproject using squash" &&
undo
'
-# 20
test_expect_success 'merge new subproj history into subdir' '
git subtree merge --prefix=subdir FETCH_HEAD &&
git branch pre-split &&
check_equal ''"$(last_commit_message)"'' "Merge commit '"'"'"$(git rev-parse sub2)"'"'"' into mainline"
'
-# 21
test_expect_success 'Check that prefix argument is required for split' '
echo "You must provide the --prefix option." > expected &&
test_must_fail git subtree split > actual 2>&1 &&
@@ -207,7 +186,6 @@ test_expect_success 'Check that prefix argument is required for split' '
rm -f expected actual
'
-# 22
test_expect_success 'Check that the <prefix> exists for a split' '
echo "'"'"'non-existent-directory'"'"'" does not exist\; use "'"'"'git subtree add'"'"'" > expected &&
test_must_fail git subtree split --prefix=non-existent-directory > actual 2>&1 &&
@@ -219,7 +197,6 @@ test_expect_success 'Check that the <prefix> exists for a split' '
# rm -f expected actual
'
-# 23
test_expect_success 'check if --message works for split+rejoin' '
spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
git branch spl1 "$spl1" &&
@@ -227,7 +204,6 @@ test_expect_success 'check if --message works for split+rejoin' '
undo
'
-# 24
test_expect_success 'check split with --branch' '
spl1=$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin) &&
undo &&
@@ -235,7 +211,6 @@ test_expect_success 'check split with --branch' '
check_equal ''"$(git rev-parse splitbr1)"'' "$spl1"
'
-# 25
test_expect_success 'check split with --branch for an existing branch' '
spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
undo &&
@@ -244,13 +219,10 @@ test_expect_success 'check split with --branch for an existing branch' '
check_equal ''"$(git rev-parse splitbr2)"'' "$spl1"
'
-# 26
test_expect_success 'check split with --branch for an incompatible branch' '
test_must_fail git subtree split --prefix subdir --onto FETCH_HEAD --branch subdir
'
-
-# 27
test_expect_success 'check split+rejoin' '
spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
undo &&
@@ -258,7 +230,6 @@ test_expect_success 'check split+rejoin' '
check_equal ''"$(last_commit_message)"'' "Split '"'"'subdir/'"'"' into commit '"'"'"$spl1"'"'"'"
'
-# 28
test_expect_success 'add main-sub8' '
create subdir/main-sub8 &&
git commit -m "main-sub8"
@@ -267,14 +238,12 @@ test_expect_success 'add main-sub8' '
# To the subproject!
cd ./subproj
-# 29
test_expect_success 'merge split into subproj' '
git fetch .. spl1 &&
git branch spl1 FETCH_HEAD &&
git merge FETCH_HEAD
'
-# 30
test_expect_success 'add sub9' '
create sub9 &&
git commit -m "sub9"
@@ -283,19 +252,16 @@ test_expect_success 'add sub9' '
# Back to mainline
cd ..
-# 31
test_expect_success 'split for sub8' '
split2=''"$(git subtree split --annotate='"'*'"' --prefix subdir/ --rejoin)"''
git branch split2 "$split2"
'
-# 32
test_expect_success 'add main-sub10' '
create subdir/main-sub10 &&
git commit -m "main-sub10"
'
-# 33
test_expect_success 'split for sub10' '
spl3=''"$(git subtree split --annotate='"'*'"' --prefix subdir --rejoin)"'' &&
git branch spl3 "$spl3"
@@ -304,7 +270,6 @@ test_expect_success 'split for sub10' '
# To the subproject!
cd ./subproj
-# 34
test_expect_success 'merge split into subproj' '
git fetch .. spl3 &&
git branch spl3 FETCH_HEAD &&
@@ -318,13 +283,11 @@ chkms_sub=$(echo $chkms | multiline | sed 's,^,subdir/,' | fixnl)
chks="sub1 sub2 sub3 sub9"
chks_sub=$(echo $chks | multiline | sed 's,^,subdir/,' | fixnl)
-# 35
test_expect_success 'make sure exactly the right set of files ends up in the subproj' '
subfiles=''"$(git ls-files | fixnl)"'' &&
check_equal "$subfiles" "$chkms $chks"
'
-# 36
test_expect_success 'make sure the subproj history *only* contains commits that affect the subdir' '
allchanges=''"$(git log --name-only --pretty=format:'"''"' | sort | fixnl)"'' &&
check_equal "$allchanges" "$chkms $chks"
@@ -333,20 +296,17 @@ test_expect_success 'make sure the subproj history *only* contains commits that
# Back to mainline
cd ..
-# 37
test_expect_success 'pull from subproj' '
git fetch ./subproj subproj-merge-spl3 &&
git branch subproj-merge-spl3 FETCH_HEAD &&
git subtree pull --prefix=subdir ./subproj subproj-merge-spl3
'
-# 38
test_expect_success 'make sure exactly the right set of files ends up in the mainline' '
mainfiles=''"$(git ls-files | fixnl)"'' &&
check_equal "$mainfiles" "$chkm $chkms_sub $chks_sub"
'
-# 39
test_expect_success 'make sure each filename changed exactly once in the entire history' '
# main-sub?? and /subdir/main-sub?? both change, because those are the
# changes that were split into their own history. And subdir/sub?? never
@@ -355,12 +315,10 @@ test_expect_success 'make sure each filename changed exactly once in the entire
check_equal "$allchanges" ''"$(echo $chkms $chkm $chks $chkms_sub | multiline | sort | fixnl)"''
'
-# 40
test_expect_success 'make sure the --rejoin commits never make it into subproj' '
check_equal ''"$(git log --pretty=format:'"'%s'"' HEAD^2 | grep -i split)"'' ""
'
-# 41
test_expect_success 'make sure no "git subtree" tagged commits make it into subproj' '
# They are meaningless to subproj since one side of the merge refers to the mainline
check_equal ''"$(git log --pretty=format:'"'%s%n%b'"' HEAD^2 | grep "git-subtree.*:")"'' ""
@@ -370,14 +328,12 @@ test_expect_success 'make sure no "git subtree" tagged commits make it into subp
mkdir test2
cd test2
-# 42
test_expect_success 'init main' '
test_create_repo main
'
cd main
-# 43
test_expect_success 'add main1' '
create main1 &&
git commit -m "main1"
@@ -385,14 +341,12 @@ test_expect_success 'add main1' '
cd ..
-# 44
test_expect_success 'init sub' '
test_create_repo sub
'
cd sub
-# 45
test_expect_success 'add sub2' '
create sub2 &&
git commit -m "sub2"
@@ -402,7 +356,6 @@ cd ../main
# check if split can find proper base without --onto
-# 46
test_expect_success 'add sub as subdir in main' '
git fetch ../sub master &&
git branch sub2 FETCH_HEAD &&
@@ -411,7 +364,6 @@ test_expect_success 'add sub as subdir in main' '
cd ../sub
-# 47
test_expect_success 'add sub3' '
create sub3 &&
git commit -m "sub3"
@@ -419,20 +371,17 @@ test_expect_success 'add sub3' '
cd ../main
-# 48
test_expect_success 'merge from sub' '
git fetch ../sub master &&
git branch sub3 FETCH_HEAD &&
git subtree merge --prefix subdir sub3
'
-# 49
test_expect_success 'add main-sub4' '
create subdir/main-sub4 &&
git commit -m "main-sub4"
'
-# 50
test_expect_success 'split for main-sub4 without --onto' '
git subtree split --prefix subdir --branch mainsub4
'
@@ -442,19 +391,16 @@ test_expect_success 'split for main-sub4 without --onto' '
# have been sub3, but it was not, because its cache was not set to
# itself)
-# 51
test_expect_success 'check that the commit parent is sub3' '
check_equal ''"$(git log --pretty=format:%P -1 mainsub4)"'' ''"$(git rev-parse sub3)"''
'
-# 52
test_expect_success 'add main-sub5' '
mkdir subdir2 &&
create subdir2/main-sub5 &&
git commit -m "main-sub5"
'
-# 53
test_expect_success 'split for main-sub5 without --onto' '
# also test that we still can split out an entirely new subtree
# if the parent of the first commit in the tree is not empty,
@@ -487,7 +433,6 @@ joincommits()
echo "$commit $all"
}
-# 54
test_expect_success 'verify one file change per commit' '
x= &&
list=''"$(git log --pretty=format:'"'commit: %H'"' | joincommits)"'' &&
--
1.8.1
^ permalink raw reply related
* [PATCH/RFC v2 5/8] contrib/subtree: Code cleaning and refactoring
From: Techlive Zheng @ 2013-01-14 3:52 UTC (permalink / raw)
To: git; +Cc: apenwarr, greened, Techlive Zheng
In-Reply-To: <1358135541-10349-1-git-send-email-techlivezheng@gmail.com>
Mostly prepare for the later tests refactoring.
Signed-off-by: Techlive Zheng <techlivezheng@gmail.com>
---
contrib/subtree/t/t7900-subtree.sh | 251 +++++++++++++++++++------------------
1 file changed, 130 insertions(+), 121 deletions(-)
diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh
index 851d00c..69bd41c 100755
--- a/contrib/subtree/t/t7900-subtree.sh
+++ b/contrib/subtree/t/t7900-subtree.sh
@@ -4,7 +4,7 @@
#
test_description='Basic porcelain support for subtrees
-This test verifies the basic operation of the merge, pull, add
+This test verifies the basic operation of the add, pull, merge
and split subcommands of git subtree.
'
@@ -18,19 +18,6 @@ create()
git add "$1"
}
-
-check_equal()
-{
- test_debug 'echo'
- test_debug "echo \"check a:\" \"{$1}\""
- test_debug "echo \" b:\" \"{$2}\""
- if [ "$1" = "$2" ]; then
- return 0
- else
- return 1
- fi
-}
-
fixnl()
{
t=""
@@ -55,6 +42,42 @@ undo()
git reset --hard HEAD~
}
+test_equal()
+{
+ test_debug 'echo'
+ test_debug "echo \"check a:\" \"{$1}\""
+ test_debug "echo \" b:\" \"{$2}\""
+ if [ "$1" = "$2" ]; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+# Make sure no patch changes more than one file.
+# The original set of commits changed only one file each.
+# A multi-file change would imply that we pruned commits
+# too aggressively.
+join_commits()
+{
+ commit=
+ all=
+ while read x y; do
+ if [ -z "$x" ]; then
+ continue
+ elif [ "$x" = "commit:" ]; then
+ if [ -n "$commit" ]; then
+ echo "$commit $all"
+ all=
+ fi
+ commit="$y"
+ else
+ all="$all $y"
+ fi
+ done
+ echo "$commit $all"
+}
+
last_commit_message()
{
git log --pretty=format:%s -1
@@ -93,7 +116,7 @@ test_expect_success 'add main4' '
create main4 &&
git commit -m "main4" &&
git branch -m master mainline &&
- git branch subdir
+ git branch init
'
test_expect_success 'fetch subproj history' '
@@ -101,40 +124,43 @@ test_expect_success 'fetch subproj history' '
git branch sub1 FETCH_HEAD
'
-test_expect_success 'no subtree exists in main tree' '
- test_must_fail git subtree merge --prefix=subdir sub1
-'
-
test_expect_success 'no pull from non-existant subtree' '
test_must_fail git subtree pull --prefix=subdir ./subproj sub1
'
-test_expect_success 'check if --message works for add' '
- git subtree add --prefix=subdir --message="Added subproject" sub1 &&
- check_equal ''"$(last_commit_message)"'' "Added subproject" &&
+test_expect_success 'no merge from non-existant subtree' '
+ test_must_fail git subtree merge --prefix=subdir FETCH_HEAD
+'
+
+test_expect_success 'add subproj as subtree into subdir/ with --prefix' '
+ git subtree add --prefix=subdir FETCH_HEAD &&
+ test_equal "$(last_commit_message)" "Add '\''subdir/'\'' from commit '\''$(git rev-parse FETCH_HEAD)'\''" &&
undo
'
-test_expect_success 'check if --message works as -m and --prefix as -P' '
- git subtree add -P subdir -m "Added subproject using git subtree" sub1 &&
- check_equal ''"$(last_commit_message)"'' "Added subproject using git subtree" &&
+test_expect_success 'add subproj as subtree into subdir/ with --prefix and --message' '
+ git subtree add --prefix=subdir --message="Added subproject" FETCH_HEAD &&
+ test_equal "$(last_commit_message)" "Added subproject" &&
undo
'
-test_expect_success 'check if --message works with squash too' '
- git subtree add -P subdir -m "Added subproject with squash" --squash sub1 &&
- check_equal ''"$(last_commit_message)"'' "Added subproject with squash" &&
+test_expect_success 'add subproj as subtree into subdir/ with --prefix as -P and --message as -m' '
+ git subtree add -P subdir -m "Added subproject" FETCH_HEAD &&
+ test_equal "$(last_commit_message)" "Added subproject" &&
undo
'
-test_expect_success 'add subproj to mainline' '
- git subtree add --prefix=subdir/ FETCH_HEAD &&
- check_equal ''"$(last_commit_message)"'' "Add '"'subdir/'"' from commit '"'"'''"$(git rev-parse sub1)"'''"'"'"
+test_expect_success 'add subproj as subtree into subdir/ with --squash and --prefix and --message' '
+ git subtree add --prefix=subdir --message="Added subproject with squash" --squash FETCH_HEAD &&
+ test_equal "$(last_commit_message)" "Added subproject with squash" &&
+ undo
'
-# this shouldn't actually do anything, since FETCH_HEAD is already a parent
-test_expect_success 'merge fetched subproj' '
- git merge -m "merge -s -ours" -s ours FETCH_HEAD
+test_expect_success 'merge the added subproj again, should do nothing' '
+ git subtree add --prefix=subdir FETCH_HEAD &&
+ # this shouldn not actually do anything, since FETCH_HEAD
+ # is already a parent
+ git merge -s ours -m "merge -s -ours" FETCH_HEAD
'
test_expect_success 'add main-sub5' '
@@ -157,25 +183,30 @@ test_expect_success 'fetch new subproj history' '
git branch sub2 FETCH_HEAD
'
-test_expect_success 'check if --message works for merge' '
- git subtree merge --prefix=subdir -m "Merged changes from subproject" sub2 &&
- check_equal ''"$(last_commit_message)"'' "Merged changes from subproject" &&
+test_expect_success 'merge new subproj history into subdir/ with --prefix' '
+ git subtree merge --prefix=subdir FETCH_HEAD &&
+ test_equal "$(last_commit_message)" "Merge commit '\''$(git rev-parse FETCH_HEAD)'\'' into mainline" &&
undo
'
-test_expect_success 'check if --message for merge works with squash too' '
- git subtree merge --prefix subdir -m "Merged changes from subproject using squash" --squash sub2 &&
- check_equal ''"$(last_commit_message)"'' "Merged changes from subproject using squash" &&
+test_expect_success 'merge new subproj history into subdir/ with --prefix and --message' '
+ git subtree merge --prefix=subdir --message="Merged changes from subproject" FETCH_HEAD &&
+ test_equal "$(last_commit_message)" "Merged changes from subproject" &&
undo
'
-test_expect_success 'merge new subproj history into subdir' '
+test_expect_success 'merge new subproj history into subdir/ with --squash and --prefix and --message' '
+ git subtree merge --prefix=subdir --message="Merged changes from subproject using squash" --squash FETCH_HEAD &&
+ test_equal "$(last_commit_message)" "Merged changes from subproject using squash" &&
+ undo
+'
+
+test_expect_success 'merge new subproj history into subdir/' '
git subtree merge --prefix=subdir FETCH_HEAD &&
- git branch pre-split &&
- check_equal ''"$(last_commit_message)"'' "Merge commit '"'"'"$(git rev-parse sub2)"'"'"' into mainline"
+ git branch pre-split
'
-test_expect_success 'Check that prefix argument is required for split' '
+test_expect_success 'split requires option --prefix' '
echo "You must provide the --prefix option." > expected &&
test_must_fail git subtree split > actual 2>&1 &&
test_debug "echo -n expected: " &&
@@ -186,48 +217,52 @@ test_expect_success 'Check that prefix argument is required for split' '
rm -f expected actual
'
-test_expect_success 'Check that the <prefix> exists for a split' '
- echo "'"'"'non-existent-directory'"'"'" does not exist\; use "'"'"'git subtree add'"'"'" > expected &&
+test_expect_success 'split requires path given by option --prefix must exist' '
+ echo "'\''non-existent-directory'\'' does not exist; use '\''git subtree add'\''" > expected &&
test_must_fail git subtree split --prefix=non-existent-directory > actual 2>&1 &&
test_debug "echo -n expected: " &&
test_debug "cat expected" &&
test_debug "echo -n actual: " &&
test_debug "cat actual" &&
- test_cmp expected actual
-# rm -f expected actual
+ test_cmp expected actual &&
+ rm -f expected actual
'
-test_expect_success 'check if --message works for split+rejoin' '
- spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
+test_expect_success 'split subdir/ with --rejoin' '
+ spl1=$(git subtree split --prefix=subdir --annotate="*") &&
git branch spl1 "$spl1" &&
- check_equal ''"$(last_commit_message)"'' "Split & rejoin" &&
+ git subtree split --prefix=subdir --annotate="*" --rejoin &&
+ test_equal "$(last_commit_message)" "Split '\''subdir/'\'' into commit '\''$spl1'\''" &&
undo
'
-test_expect_success 'check split with --branch' '
- spl1=$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin) &&
+test_expect_success 'split subdir/ with --rejoin and --message' '
+ git subtree split --prefix=subdir --message="Split & rejoin" --annotate="*" --rejoin &&
+ test_equal "$(last_commit_message)" "Split & rejoin" &&
+ undo
+'
+
+test_expect_success 'split subdir/ with --branch' '
+ spl1=$(git subtree split --prefix=subdir --message="Split & rejoin" --annotate="*" --rejoin) &&
undo &&
- git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --branch splitbr1 &&
- check_equal ''"$(git rev-parse splitbr1)"'' "$spl1"
+ git subtree split --prefix=subdir --annotate="*" --branch splitbr1 &&
+ test_equal "$(git rev-parse splitbr1)" "$spl1"
'
-test_expect_success 'check split with --branch for an existing branch' '
- spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
+test_expect_success 'split subdir/ with --branch for an existing branch' '
+ spl1=$(git subtree split --prefix=subdir --annotate="*" --message="Split & rejoin" --rejoin) &&
undo &&
git branch splitbr2 sub1 &&
- git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --branch splitbr2 &&
- check_equal ''"$(git rev-parse splitbr2)"'' "$spl1"
+ git subtree split --prefix=subdir --annotate="*" --branch splitbr2 &&
+ test_equal "$(git rev-parse splitbr2)" "$spl1"
'
-test_expect_success 'check split with --branch for an incompatible branch' '
- test_must_fail git subtree split --prefix subdir --onto FETCH_HEAD --branch subdir
+test_expect_success 'split subdir/ with --branch for an incompatible branch' '
+ test_must_fail git subtree split --prefix=subdir --branch init
'
-test_expect_success 'check split+rejoin' '
- spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
- undo &&
- git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --rejoin &&
- check_equal ''"$(last_commit_message)"'' "Split '"'"'subdir/'"'"' into commit '"'"'"$spl1"'"'"'"
+test_expect_success 'split and rejoin' '
+ git subtree split --prefix=subdir --annotate="*" --rejoin
'
test_expect_success 'add main-sub8' '
@@ -253,8 +288,8 @@ test_expect_success 'add sub9' '
cd ..
test_expect_success 'split for sub8' '
- split2=''"$(git subtree split --annotate='"'*'"' --prefix subdir/ --rejoin)"''
- git branch split2 "$split2"
+ spl2=$(git subtree split --prefix=subdir/ --annotate="*" --rejoin) &&
+ git branch spl2 "$spl2"
'
test_expect_success 'add main-sub10' '
@@ -263,7 +298,7 @@ test_expect_success 'add main-sub10' '
'
test_expect_success 'split for sub10' '
- spl3=''"$(git subtree split --annotate='"'*'"' --prefix subdir --rejoin)"'' &&
+ spl3=$(git subtree split --prefix=subdir --annotate="*" --rejoin) &&
git branch spl3 "$spl3"
'
@@ -284,13 +319,13 @@ chks="sub1 sub2 sub3 sub9"
chks_sub=$(echo $chks | multiline | sed 's,^,subdir/,' | fixnl)
test_expect_success 'make sure exactly the right set of files ends up in the subproj' '
- subfiles=''"$(git ls-files | fixnl)"'' &&
- check_equal "$subfiles" "$chkms $chks"
+ subfiles=$(git ls-files | fixnl) &&
+ test_equal "$subfiles" "$chkms $chks"
'
-test_expect_success 'make sure the subproj history *only* contains commits that affect the subdir' '
- allchanges=''"$(git log --name-only --pretty=format:'"''"' | sort | fixnl)"'' &&
- check_equal "$allchanges" "$chkms $chks"
+test_expect_success 'make sure the subproj *only* contains commits that affect the subdir' '
+ allchanges=$(git log --name-only --pretty=format:"" | sort | fixnl) &&
+ test_equal "$allchanges" "$chkms $chks"
'
# Back to mainline
@@ -303,25 +338,25 @@ test_expect_success 'pull from subproj' '
'
test_expect_success 'make sure exactly the right set of files ends up in the mainline' '
- mainfiles=''"$(git ls-files | fixnl)"'' &&
- check_equal "$mainfiles" "$chkm $chkms_sub $chks_sub"
+ mainfiles=$(git ls-files | fixnl) &&
+ test_equal "$mainfiles" "$chkm $chkms_sub $chks_sub"
'
test_expect_success 'make sure each filename changed exactly once in the entire history' '
# main-sub?? and /subdir/main-sub?? both change, because those are the
# changes that were split into their own history. And subdir/sub?? never
# change, since they were *only* changed in the subtree branch.
- allchanges=''"$(git log --name-only --pretty=format:'"''"' | sort | fixnl)"'' &&
- check_equal "$allchanges" ''"$(echo $chkms $chkm $chks $chkms_sub | multiline | sort | fixnl)"''
+ allchanges=$(git log --name-only --pretty=format:"" | sort | fixnl) &&
+ test_equal "$allchanges" "$(echo $chkms $chkm $chks $chkms_sub | multiline | sort | fixnl)"
'
test_expect_success 'make sure the --rejoin commits never make it into subproj' '
- check_equal ''"$(git log --pretty=format:'"'%s'"' HEAD^2 | grep -i split)"'' ""
+ test_equal "$(git log --pretty=format:"%s" HEAD^2 | grep -i split)" ""
'
test_expect_success 'make sure no "git subtree" tagged commits make it into subproj' '
# They are meaningless to subproj since one side of the merge refers to the mainline
- check_equal ''"$(git log --pretty=format:'"'%s%n%b'"' HEAD^2 | grep "git-subtree.*:")"'' ""
+ test_equal "$(git log --pretty=format:"%s%n%b" HEAD^2 | grep "git-subtree.*:")" ""
'
# prepare second pair of repositories
@@ -359,7 +394,7 @@ cd ../main
test_expect_success 'add sub as subdir in main' '
git fetch ../sub master &&
git branch sub2 FETCH_HEAD &&
- git subtree add --prefix subdir sub2
+ git subtree add --prefix=subdir sub2
'
cd ../sub
@@ -374,7 +409,7 @@ cd ../main
test_expect_success 'merge from sub' '
git fetch ../sub master &&
git branch sub3 FETCH_HEAD &&
- git subtree merge --prefix subdir sub3
+ git subtree merge --prefix=subdir sub3
'
test_expect_success 'add main-sub4' '
@@ -383,7 +418,7 @@ test_expect_success 'add main-sub4' '
'
test_expect_success 'split for main-sub4 without --onto' '
- git subtree split --prefix subdir --branch mainsub4
+ git subtree split --prefix=subdir --branch mainsub4
'
# at this point, the new commit parent should be sub3 if it is not,
@@ -392,7 +427,7 @@ test_expect_success 'split for main-sub4 without --onto' '
# itself)
test_expect_success 'check that the commit parent is sub3' '
- check_equal ''"$(git log --pretty=format:%P -1 mainsub4)"'' ''"$(git rev-parse sub3)"''
+ test_equal "$(git log --pretty=format:%P -1 mainsub4)" "$(git rev-parse sub3)"
'
test_expect_success 'add main-sub5' '
@@ -405,49 +440,23 @@ test_expect_success 'split for main-sub5 without --onto' '
# also test that we still can split out an entirely new subtree
# if the parent of the first commit in the tree is not empty,
# then the new subtree has accidently been attached to something
- git subtree split --prefix subdir2 --branch mainsub5 &&
- check_equal ''"$(git log --pretty=format:%P -1 mainsub5)"'' ""
+ git subtree split --prefix=subdir2 --branch mainsub5 &&
+ test_equal "$(git log --pretty=format:%P -1 mainsub5)" ""
'
-# make sure no patch changes more than one file. The original set of commits
-# changed only one file each. A multi-file change would imply that we pruned
-# commits too aggressively.
-joincommits()
-{
- commit=
- all=
- while read x y; do
- #echo "{$x}" >&2
- if [ -z "$x" ]; then
- continue
- elif [ "$x" = "commit:" ]; then
- if [ -n "$commit" ]; then
- echo "$commit $all"
- all=
- fi
- commit="$y"
- else
- all="$all $y"
- fi
- done
- echo "$commit $all"
-}
-
test_expect_success 'verify one file change per commit' '
x= &&
- list=''"$(git log --pretty=format:'"'commit: %H'"' | joincommits)"'' &&
-# test_debug "echo HERE" &&
-# test_debug "echo ''"$list"''" &&
- (git log --pretty=format:'"'commit: %H'"' | joincommits |
- ( while read commit a b; do
- test_debug "echo Verifying commit "''"$commit"''
- test_debug "echo a: "''"$a"''
- test_debug "echo b: "''"$b"''
- check_equal "$b" ""
+ git log --pretty=format:"commit: %H" | join_commits |
+ (
+ while read commit a b; do
+ test_debug "echo Verifying commit $commit"
+ test_debug "echo a: $a"
+ test_debug "echo b: $b"
+ test_equal "$b" ""
x=1
done
- check_equal "$x" 1
- ))
+ test_equal "$x" 1
+ )
'
test_done
--
1.8.1
^ permalink raw reply related
* [PATCH/RFC v2 8/8] contrib/subtree: Handle '--prefix' argument with a slash appended
From: Techlive Zheng @ 2013-01-14 3:52 UTC (permalink / raw)
To: git; +Cc: apenwarr, greened, Techlive Zheng
In-Reply-To: <1358135541-10349-1-git-send-email-techlivezheng@gmail.com>
'git subtree merge' will fail if the argument of '--prefix' has a slash
appended.
Signed-off-by: Techlive Zheng <techlivezheng@gmail.com>
---
contrib/subtree/git-subtree.sh | 2 +-
contrib/subtree/t/t7900-subtree.sh | 19 +++++++++++++++++++
2 files changed, 20 insertions(+), 1 deletion(-)
diff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh
index d529a76..40100e5 100755
--- a/contrib/subtree/git-subtree.sh
+++ b/contrib/subtree/git-subtree.sh
@@ -83,7 +83,7 @@ while [ $# -gt 0 ]; do
--annotate) annotate="$1"; shift ;;
--no-annotate) annotate= ;;
-b) branch="$1"; shift ;;
- -P) prefix="$1"; shift ;;
+ -P) prefix="${1%/}"; shift ;;
-m) message="$1"; shift ;;
--no-prefix) prefix= ;;
--onto) onto="$1"; shift ;;
diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh
index 232ed89..297dac4 100755
--- a/contrib/subtree/t/t7900-subtree.sh
+++ b/contrib/subtree/t/t7900-subtree.sh
@@ -238,6 +238,25 @@ test_expect_success 'merge new subproj history into subdir/ with --squash and --
)
'
+test_expect_success 'merge new subproj history into subdir/ with a slash appended to the argument of --prefix' '
+ test_create_repo "$test_count" &&
+ test_create_repo "$test_count/subproj" &&
+ test_create_commit "$test_count" main1 &&
+ test_create_commit "$test_count/subproj" sub1 &&
+ (
+ cd "$test_count" &&
+ git fetch ./subproj master &&
+ git subtree add --prefix=subdir/ FETCH_HEAD
+ ) &&
+ test_create_commit "$test_count/subproj" sub2 &&
+ (
+ cd "$test_count" &&
+ git fetch ./subproj master &&
+ git subtree merge --prefix=subdir/ FETCH_HEAD &&
+ test_equal "$(last_commit_message)" "Merge commit '\''$(git rev-parse FETCH_HEAD)'\''"
+ )
+'
+
#
# Tests for 'git subtree split'
#
--
1.8.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox