Index: user/uqs/git_conv/svn2git/.git/FETCH_HEAD =================================================================== --- user/uqs/git_conv/svn2git/.git/FETCH_HEAD (revision 352592) +++ user/uqs/git_conv/svn2git/.git/FETCH_HEAD (revision 352593) @@ -1,2 +1,10 @@ 437beb493db3b1cb532ca5807264cd4403a44157 branch 'master' of https://github.com/svn-all-fast-export/svn2git 4d494101dc0a6abb918695422d2e2f0cd400d9b7 not-for-merge branch 'missing-revprops' of https://github.com/svn-all-fast-export/svn2git +5392048fb1f68fe1cb0681c7b9fef1802dd74d06 branch 'master' of https://github.com/svn-all-fast-export/svn2git +4d494101dc0a6abb918695422d2e2f0cd400d9b7 not-for-merge branch 'missing-revprops' of https://github.com/svn-all-fast-export/svn2git +5caa913b3ac6fea8ea3a92558889a12e7feed21e not-for-merge tag '1.0.11' of https://github.com/svn-all-fast-export/svn2git +83d6f8d2b28e7db5c1ac959df96fc74af363ab92 not-for-merge tag '1.0.12' of https://github.com/svn-all-fast-export/svn2git +87a645e2cb2f2f74e4067b2aa71c7d0bd9856cb6 not-for-merge tag '1.0.13' of https://github.com/svn-all-fast-export/svn2git +c9e7c4a98e8ba0b8269e5a7b9d3ac68b02d0406e not-for-merge tag '1.0.14' of https://github.com/svn-all-fast-export/svn2git +58378dcd8d758ccac2465547457dc586e2443fab not-for-merge tag '1.0.15' of https://github.com/svn-all-fast-export/svn2git +3d583c89965dd238e5ea4ffad721085bba4542a5 not-for-merge tag '1.0.16' of https://github.com/svn-all-fast-export/svn2git Index: user/uqs/git_conv/svn2git/.git/ORIG_HEAD =================================================================== --- user/uqs/git_conv/svn2git/.git/ORIG_HEAD (revision 352592) +++ user/uqs/git_conv/svn2git/.git/ORIG_HEAD (revision 352593) @@ -1 +1 @@ -437beb493db3b1cb532ca5807264cd4403a44157 +e9699437cad516b3b9494443f3132c15b8ef4485 Index: user/uqs/git_conv/svn2git/.git/refs/remotes/svn-all-fast-export/master =================================================================== --- user/uqs/git_conv/svn2git/.git/refs/remotes/svn-all-fast-export/master (revision 352592) +++ user/uqs/git_conv/svn2git/.git/refs/remotes/svn-all-fast-export/master (revision 352593) @@ -1 +1 @@ -437beb493db3b1cb532ca5807264cd4403a44157 +5392048fb1f68fe1cb0681c7b9fef1802dd74d06 Index: user/uqs/git_conv/svn2git/Makefile =================================================================== --- user/uqs/git_conv/svn2git/Makefile (revision 352592) +++ user/uqs/git_conv/svn2git/Makefile (revision 352593) @@ -1,128 +1,162 @@ ############################################################################# # Makefile for building: fast-export2 -# Generated by qmake (2.01a) (Qt 4.8.7) on: Thu Jan 12 12:51:50 2017 +# Generated by qmake (3.1) (Qt 5.12.2) # Project: fast-export2.pro # Template: subdirs -# Command: /usr/local/bin/qmake-qt4 -o Makefile fast-export2.pro +# Command: /usr/local/lib/qt5/bin/qmake -o Makefile fast-export2.pro ############################################################################# -first: make_default MAKEFILE = Makefile -QMAKE = /usr/local/bin/qmake-qt4 + +EQ = = + +first: make_first +QMAKE = /usr/local/lib/qt5/bin/qmake DEL_FILE = rm -f CHK_DIR_EXISTS= test -d MKDIR = mkdir -p COPY = cp -f COPY_FILE = cp -f COPY_DIR = cp -f -R INSTALL_FILE = install -m 644 -p INSTALL_PROGRAM = install -m 755 -p -INSTALL_DIR = $(COPY_DIR) +INSTALL_DIR = cp -f -R +QINSTALL = /usr/local/lib/qt5/bin/qmake -install qinstall +QINSTALL_PROGRAM = /usr/local/lib/qt5/bin/qmake -install qinstall -exe DEL_FILE = rm -f SYMLINK = ln -f -s DEL_DIR = rmdir MOVE = mv -f -CHK_DIR_EXISTS= test -d -MKDIR = mkdir -p +TAR = tar -cf +COMPRESS = gzip -9f +DISTNAME = fast-export21.0.0 +DISTDIR = /data/src/svn2git/.tmp/fast-export21.0.0 SUBTARGETS = \ sub-src -src/$(MAKEFILE): - @$(CHK_DIR_EXISTS) src/ || $(MKDIR) src/ - cd src/ && $(QMAKE) /data/src/git_conv/svn2git/src/src.pro -o $(MAKEFILE) + sub-src-qmake_all: FORCE - @$(CHK_DIR_EXISTS) src/ || $(MKDIR) src/ - cd src/ && $(QMAKE) /data/src/git_conv/svn2git/src/src.pro -o $(MAKEFILE) -sub-src: src/$(MAKEFILE) FORCE - cd src/ && $(MAKE) -f $(MAKEFILE) -sub-src-make_default: src/$(MAKEFILE) FORCE - cd src/ && $(MAKE) -f $(MAKEFILE) -sub-src-make_first: src/$(MAKEFILE) FORCE - cd src/ && $(MAKE) -f $(MAKEFILE) first -sub-src-all: src/$(MAKEFILE) FORCE - cd src/ && $(MAKE) -f $(MAKEFILE) all -sub-src-clean: src/$(MAKEFILE) FORCE - cd src/ && $(MAKE) -f $(MAKEFILE) clean -sub-src-distclean: src/$(MAKEFILE) FORCE - cd src/ && $(MAKE) -f $(MAKEFILE) distclean -sub-src-install_subtargets: src/$(MAKEFILE) FORCE - cd src/ && $(MAKE) -f $(MAKEFILE) install -sub-src-uninstall_subtargets: src/$(MAKEFILE) FORCE - cd src/ && $(MAKE) -f $(MAKEFILE) uninstall + @test -d src/ || mkdir -p src/ + cd src/ && $(QMAKE) -o Makefile /data/src/svn2git/src/src.pro + cd src/ && $(MAKE) -f Makefile qmake_all +sub-src: FORCE + @test -d src/ || mkdir -p src/ + cd src/ && ( test -e Makefile || $(QMAKE) -o Makefile /data/src/svn2git/src/src.pro ) && $(MAKE) -f Makefile +sub-src-make_first: FORCE + @test -d src/ || mkdir -p src/ + cd src/ && ( test -e Makefile || $(QMAKE) -o Makefile /data/src/svn2git/src/src.pro ) && $(MAKE) -f Makefile +sub-src-all: FORCE + @test -d src/ || mkdir -p src/ + cd src/ && ( test -e Makefile || $(QMAKE) -o Makefile /data/src/svn2git/src/src.pro ) && $(MAKE) -f Makefile all +sub-src-clean: FORCE + @test -d src/ || mkdir -p src/ + cd src/ && ( test -e Makefile || $(QMAKE) -o Makefile /data/src/svn2git/src/src.pro ) && $(MAKE) -f Makefile clean +sub-src-distclean: FORCE + @test -d src/ || mkdir -p src/ + cd src/ && ( test -e Makefile || $(QMAKE) -o Makefile /data/src/svn2git/src/src.pro ) && $(MAKE) -f Makefile distclean +sub-src-install_subtargets: FORCE + @test -d src/ || mkdir -p src/ + cd src/ && ( test -e Makefile || $(QMAKE) -o Makefile /data/src/svn2git/src/src.pro ) && $(MAKE) -f Makefile install +sub-src-uninstall_subtargets: FORCE + @test -d src/ || mkdir -p src/ + cd src/ && ( test -e Makefile || $(QMAKE) -o Makefile /data/src/svn2git/src/src.pro ) && $(MAKE) -f Makefile uninstall -Makefile: fast-export2.pro /usr/local/share/qt4/mkspecs/freebsd-clang/qmake.conf /usr/local/share/qt4/mkspecs/common/unix.conf \ - /usr/local/share/qt4/mkspecs/common/freebsd.conf \ - /usr/local/share/qt4/mkspecs/common/gcc-base.conf \ - /usr/local/share/qt4/mkspecs/common/gcc-base-unix.conf \ - /usr/local/share/qt4/mkspecs/common/clang.conf \ - /usr/local/share/qt4/mkspecs/common/clang-unix.conf \ - /usr/local/share/qt4/mkspecs/qconfig.pri \ - /usr/local/share/qt4/mkspecs/features/qt_functions.prf \ - /usr/local/share/qt4/mkspecs/features/qt_config.prf \ - /usr/local/share/qt4/mkspecs/features/exclusive_builds.prf \ - /usr/local/share/qt4/mkspecs/features/default_pre.prf \ - /usr/local/share/qt4/mkspecs/features/release.prf \ - /usr/local/share/qt4/mkspecs/features/default_post.prf \ - /usr/local/share/qt4/mkspecs/features/shared.prf \ - /usr/local/share/qt4/mkspecs/features/unix/gdb_dwarf_index.prf \ - /usr/local/share/qt4/mkspecs/features/warn_on.prf \ - /usr/local/share/qt4/mkspecs/features/qt.prf \ - /usr/local/share/qt4/mkspecs/features/unix/thread.prf \ - /usr/local/share/qt4/mkspecs/features/moc.prf \ - /usr/local/share/qt4/mkspecs/features/resources.prf \ - /usr/local/share/qt4/mkspecs/features/uic.prf \ - /usr/local/share/qt4/mkspecs/features/yacc.prf \ - /usr/local/share/qt4/mkspecs/features/lex.prf \ - /usr/local/share/qt4/mkspecs/features/include_source_dir.prf +Makefile: fast-export2.pro /usr/local/lib/qt5/mkspecs/freebsd-clang/qmake.conf /usr/local/lib/qt5/mkspecs/features/spec_pre.prf \ + /usr/local/lib/qt5/mkspecs/common/unix.conf \ + /usr/local/lib/qt5/mkspecs/common/bsd/bsd.conf \ + /usr/local/lib/qt5/mkspecs/common/sanitize.conf \ + /usr/local/lib/qt5/mkspecs/common/gcc-base.conf \ + /usr/local/lib/qt5/mkspecs/common/gcc-base-unix.conf \ + /usr/local/lib/qt5/mkspecs/common/clang.conf \ + /usr/local/lib/qt5/mkspecs/qconfig.pri \ + /usr/local/lib/qt5/mkspecs/modules/qt_config_core.pri \ + /usr/local/lib/qt5/mkspecs/modules/qt_lib_core.pri \ + /usr/local/lib/qt5/mkspecs/modules/qt_lib_core_private.pri \ + /usr/local/lib/qt5/mkspecs/features/qt_functions.prf \ + /usr/local/lib/qt5/mkspecs/features/qt_config.prf \ + /usr/local/lib/qt5/mkspecs/freebsd-clang/qmake.conf \ + /usr/local/lib/qt5/mkspecs/features/spec_post.prf \ + .qmake.stash \ + /usr/local/lib/qt5/mkspecs/features/exclusive_builds.prf \ + /usr/local/lib/qt5/mkspecs/features/toolchain.prf \ + /usr/local/lib/qt5/mkspecs/features/default_pre.prf \ + /usr/local/lib/qt5/mkspecs/features/resolve_config.prf \ + /usr/local/lib/qt5/mkspecs/features/default_post.prf \ + /usr/local/lib/qt5/mkspecs/features/warn_on.prf \ + /usr/local/lib/qt5/mkspecs/features/qmake_use.prf \ + /usr/local/lib/qt5/mkspecs/features/file_copies.prf \ + /usr/local/lib/qt5/mkspecs/features/testcase_targets.prf \ + /usr/local/lib/qt5/mkspecs/features/exceptions.prf \ + /usr/local/lib/qt5/mkspecs/features/yacc.prf \ + /usr/local/lib/qt5/mkspecs/features/lex.prf \ + fast-export2.pro $(QMAKE) -o Makefile fast-export2.pro -/usr/local/share/qt4/mkspecs/common/unix.conf: -/usr/local/share/qt4/mkspecs/common/freebsd.conf: -/usr/local/share/qt4/mkspecs/common/gcc-base.conf: -/usr/local/share/qt4/mkspecs/common/gcc-base-unix.conf: -/usr/local/share/qt4/mkspecs/common/clang.conf: -/usr/local/share/qt4/mkspecs/common/clang-unix.conf: -/usr/local/share/qt4/mkspecs/qconfig.pri: -/usr/local/share/qt4/mkspecs/features/qt_functions.prf: -/usr/local/share/qt4/mkspecs/features/qt_config.prf: -/usr/local/share/qt4/mkspecs/features/exclusive_builds.prf: -/usr/local/share/qt4/mkspecs/features/default_pre.prf: -/usr/local/share/qt4/mkspecs/features/release.prf: -/usr/local/share/qt4/mkspecs/features/default_post.prf: -/usr/local/share/qt4/mkspecs/features/shared.prf: -/usr/local/share/qt4/mkspecs/features/unix/gdb_dwarf_index.prf: -/usr/local/share/qt4/mkspecs/features/warn_on.prf: -/usr/local/share/qt4/mkspecs/features/qt.prf: -/usr/local/share/qt4/mkspecs/features/unix/thread.prf: -/usr/local/share/qt4/mkspecs/features/moc.prf: -/usr/local/share/qt4/mkspecs/features/resources.prf: -/usr/local/share/qt4/mkspecs/features/uic.prf: -/usr/local/share/qt4/mkspecs/features/yacc.prf: -/usr/local/share/qt4/mkspecs/features/lex.prf: -/usr/local/share/qt4/mkspecs/features/include_source_dir.prf: -qmake: qmake_all FORCE +/usr/local/lib/qt5/mkspecs/features/spec_pre.prf: +/usr/local/lib/qt5/mkspecs/common/unix.conf: +/usr/local/lib/qt5/mkspecs/common/bsd/bsd.conf: +/usr/local/lib/qt5/mkspecs/common/sanitize.conf: +/usr/local/lib/qt5/mkspecs/common/gcc-base.conf: +/usr/local/lib/qt5/mkspecs/common/gcc-base-unix.conf: +/usr/local/lib/qt5/mkspecs/common/clang.conf: +/usr/local/lib/qt5/mkspecs/qconfig.pri: +/usr/local/lib/qt5/mkspecs/modules/qt_config_core.pri: +/usr/local/lib/qt5/mkspecs/modules/qt_lib_core.pri: +/usr/local/lib/qt5/mkspecs/modules/qt_lib_core_private.pri: +/usr/local/lib/qt5/mkspecs/features/qt_functions.prf: +/usr/local/lib/qt5/mkspecs/features/qt_config.prf: +/usr/local/lib/qt5/mkspecs/freebsd-clang/qmake.conf: +/usr/local/lib/qt5/mkspecs/features/spec_post.prf: +.qmake.stash: +/usr/local/lib/qt5/mkspecs/features/exclusive_builds.prf: +/usr/local/lib/qt5/mkspecs/features/toolchain.prf: +/usr/local/lib/qt5/mkspecs/features/default_pre.prf: +/usr/local/lib/qt5/mkspecs/features/resolve_config.prf: +/usr/local/lib/qt5/mkspecs/features/default_post.prf: +/usr/local/lib/qt5/mkspecs/features/warn_on.prf: +/usr/local/lib/qt5/mkspecs/features/qmake_use.prf: +/usr/local/lib/qt5/mkspecs/features/file_copies.prf: +/usr/local/lib/qt5/mkspecs/features/testcase_targets.prf: +/usr/local/lib/qt5/mkspecs/features/exceptions.prf: +/usr/local/lib/qt5/mkspecs/features/yacc.prf: +/usr/local/lib/qt5/mkspecs/features/lex.prf: +fast-export2.pro: +qmake: FORCE @$(QMAKE) -o Makefile fast-export2.pro qmake_all: sub-src-qmake_all FORCE -make_default: sub-src-make_default FORCE -make_first: sub-src-make_first FORCE -all: sub-src-all FORCE -clean: sub-src-clean FORCE -distclean: sub-src-distclean FORCE +make_first: sub-src-make_first FORCE +all: sub-src-all FORCE +clean: sub-src-clean FORCE +distclean: sub-src-distclean FORCE -$(DEL_FILE) Makefile + -$(DEL_FILE) .qmake.stash install_subtargets: sub-src-install_subtargets FORCE uninstall_subtargets: sub-src-uninstall_subtargets FORCE -sub-src-check: src/$(MAKEFILE) - cd src/ && $(MAKE) -f $(MAKEFILE) check +sub-src-check: + @test -d src/ || mkdir -p src/ + cd src/ && ( test -e Makefile || $(QMAKE) -o Makefile /data/src/svn2git/src/src.pro ) && $(MAKE) -f Makefile check check: sub-src-check -mocclean: compiler_moc_header_clean compiler_moc_source_clean +sub-src-benchmark: + @test -d src/ || mkdir -p src/ + cd src/ && ( test -e Makefile || $(QMAKE) -o Makefile /data/src/svn2git/src/src.pro ) && $(MAKE) -f Makefile benchmark +benchmark: sub-src-benchmark +install:install_subtargets FORCE -mocables: compiler_moc_header_make_all compiler_moc_source_make_all -install: install_subtargets FORCE +uninstall: uninstall_subtargets FORCE -uninstall: uninstall_subtargets FORCE - FORCE: + +dist: distdir FORCE + (cd `dirname $(DISTDIR)` && $(TAR) $(DISTNAME).tar $(DISTNAME) && $(COMPRESS) $(DISTNAME).tar) && $(MOVE) `dirname $(DISTDIR)`/$(DISTNAME).tar.gz . && $(DEL_FILE) -r $(DISTDIR) + +distdir: sub-src-distdir FORCE + @test -d $(DISTDIR) || mkdir -p $(DISTDIR) + $(COPY_FILE) --parents /usr/local/lib/qt5/mkspecs/features/spec_pre.prf /usr/local/lib/qt5/mkspecs/common/unix.conf /usr/local/lib/qt5/mkspecs/common/bsd/bsd.conf /usr/local/lib/qt5/mkspecs/common/sanitize.conf /usr/local/lib/qt5/mkspecs/common/gcc-base.conf /usr/local/lib/qt5/mkspecs/common/gcc-base-unix.conf /usr/local/lib/qt5/mkspecs/common/clang.conf /usr/local/lib/qt5/mkspecs/qconfig.pri /usr/local/lib/qt5/mkspecs/modules/qt_config_core.pri /usr/local/lib/qt5/mkspecs/modules/qt_lib_core.pri /usr/local/lib/qt5/mkspecs/modules/qt_lib_core_private.pri /usr/local/lib/qt5/mkspecs/features/qt_functions.prf /usr/local/lib/qt5/mkspecs/features/qt_config.prf /usr/local/lib/qt5/mkspecs/freebsd-clang/qmake.conf /usr/local/lib/qt5/mkspecs/features/spec_post.prf .qmake.stash /usr/local/lib/qt5/mkspecs/features/exclusive_builds.prf /usr/local/lib/qt5/mkspecs/features/toolchain.prf /usr/local/lib/qt5/mkspecs/features/default_pre.prf /usr/local/lib/qt5/mkspecs/features/resolve_config.prf /usr/local/lib/qt5/mkspecs/features/default_post.prf /usr/local/lib/qt5/mkspecs/features/warn_on.prf /usr/local/lib/qt5/mkspecs/features/qmake_use.prf /usr/local/lib/qt5/mkspecs/features/file_copies.prf /usr/local/lib/qt5/mkspecs/features/testcase_targets.prf /usr/local/lib/qt5/mkspecs/features/exceptions.prf /usr/local/lib/qt5/mkspecs/features/yacc.prf /usr/local/lib/qt5/mkspecs/features/lex.prf fast-export2.pro $(DISTDIR)/ + +sub-src-distdir: FORCE + @test -d src/ || mkdir -p src/ + cd src/ && ( test -e Makefile || $(QMAKE) -o Makefile /data/src/svn2git/src/src.pro ) && $(MAKE) -e -f Makefile distdir DISTDIR=$(DISTDIR)/src Index: user/uqs/git_conv/svn2git/README.md =================================================================== --- user/uqs/git_conv/svn2git/README.md (revision 352592) +++ user/uqs/git_conv/svn2git/README.md (revision 352593) @@ -1,40 +1,103 @@ svn-all-fast-export aka svn2git =============================== This project contains all the tools required to do a conversion of an svn repository (server side, not a checkout) to one or more git repositories. This is the tool used to convert KDE's Subversion into multiple Git repositories. You can find more description and usage examples at https://techbase.kde.org/Projects/MoveToGit/UsingSvn2Git How does it work ---------------- The svn2git repository gets you an application that will do the actual conversion. The conversion exists of looping over each and every commit in the subversion repository and matching the changes to a ruleset after which the changes are applied to a certain path in a git repo. The ruleset can specify which git repository to use and thus you can have more than one git repository as a result of running the conversion. Also noteworthy is that you can have a rule that, for example, changes in svnrepo/branches/foo/2.1/ will appear as a git-branch in a repository. If you have a proper ruleset the tool will create the git repositories for you and show progress while converting commit by commit. After it is done you likely want to run `git repack -a -d -f` to compress the pack file as it can get quite big. +Running as Docker image +----------------------- +Just mount your SVN folder, plus another working directory where Git repository will be created. +Sample usage with input mounted in /tmp and output produced in /workdir: +``` +docker build -t svn2git . +docker run --rm -it -v `pwd`/workdir:/workdir -v /var/lib/svn/project1:/tmp/svn -v `pwd`/conf:/tmp/conf svn2git /usr/local/svn2git/svn-all-fast-export --identity-map /tmp/conf/project1.authors --rules /tmp/conf/project1.rules --add-metadata --svn-branches --debug-rules --svn-ignore --empty-dirs /tmp/svn/ +``` + Building the tool ----------------- Run `qmake && make`. You get `./svn-all-fast-export`. (Do a checkout of the repo .git' and run qmake and make. You can only build it after having installed libsvn-dev, and naturally Qt. Running the command will give you all the options you can pass to the tool.) +You will need to have some packages to compile it. For Ubuntu distros, use this command to install them all: +`sudo apt-get install build-essential subversion git qtchooser qt5-default libapr1 libapr1-dev libsvn-dev` + KDE --- there is a repository kde-ruleset which has several example files and one file that should become the final ruleset for the whole of KDE called 'kde-rules-main'. Write the Rules --------------- You need to write a rules file that describes how to slice the Subversion history into Git repositories and branches. See https://techbase.kde.org/Projects/MoveToGit/UsingSvn2Git. The rules are also documented in the 'samples' directory of the svn2git repository. Feel free to add more documentation here as well. + +Rules +----- +### `create respository` + +``` +create repository REPOSITORY NAME + [PARAMETERS...] +end repository +``` + +`PARAMETERS` is any number of: + +- `repository TARGET REPOSITORY` Creates a forwarding repository , which allows for redirecting to another repository, typically with some `prefix`. +- `prefix PREFIX` prefixes each file with `PREFIX`, allowing for merging repositories. +- `description DESCRIPTION TEXT` writes a `DESCRIPTION TEXT` to the `description` file in the repository + +### `match` + +``` +match REGEX + [PARAMETERS...] +end match +``` + +Creates a rule that matches paths by `REGEX` and applies some `PARAMETERS` to them. Matching groups can be created, and the values used in the parameters. + +`PARAMETERS` is any number of: + +- `repository TARGET REPOSITORY` determines the repository +- `branch BRANCH NAME` determines which branch this path will be placed in. Can also be used to make lightweight tags with `refs/tags/TAG NAME` although note that tags in SVN are not always a single commit, and will not be created correctly unless they are a single copy from somewhere else, with no further changes. See also `annotate true` to make them annotated tags. +- `[min|max] revision REVISION NUMBER` only match if revision is above/below the specified revision number +- `prefix PREFIX` prefixes each file with `PREFIX`, allowing for merging repositories. Same as when used in a `create repository` stanza. + - Note that this will create a separate commit for each prefix matched, even if they were in the same SVN revision. +- `substitute [repository|branch] s/PATTERN/REPLACEMENT/` performs a regex substitution on the repository or branch name. Useful when eliminating characters not supported in git branch names. +- `action ACTION` determines the action to take, from the below three: + + - `export` I have no idea what this does + - `ignore` ignores this path + - `recurse` tells svn2git to ignore this path and continue searching it's children. + +- `annotate true` creates annotated tags instead of lightweight tags + +### `include FILENAME` + +Include the contents of another rules file + +### `declare VAR=VALUE` + +Define variables that can be referenced later. `${VAR}` in any line will be replaced by `VALUE`. + Work flow --------- Please feel free to fill this section in. Some SVN tricks --------------- You can access your newly rsynced SVN repo with commands like `svn ls file:///path/to/repo/trunk/KDE`. A common issue is tracking when an item left playground for kdereview and then went from kdereview to its final destination. There is no straightforward way to do this. So the following command comes in handy: `svn log -v file:///path/to/repo/kde-svn/kde/trunk/kdereview | grep /trunk/kdereview/mplayerthumbs -A 5 -B 5` This will print all commits relevant to the package you are trying to track. You can also pipe the above command to head or tail to see the the first and last commit it was in that directory. Index: user/uqs/git_conv/svn2git/fast-export2.pro =================================================================== --- user/uqs/git_conv/svn2git/fast-export2.pro (revision 352592) +++ user/uqs/git_conv/svn2git/fast-export2.pro (revision 352593) @@ -1,8 +1,4 @@ -###################################################################### -# Automatically generated by qmake (2.01a) dim. dec. 23 13:48:52 2007 -###################################################################### - TEMPLATE = subdirs # Directories SUBDIRS = src Index: user/uqs/git_conv/svn2git/src/Makefile =================================================================== --- user/uqs/git_conv/svn2git/src/Makefile (revision 352592) +++ user/uqs/git_conv/svn2git/src/Makefile (revision 352593) @@ -1,242 +1,717 @@ ############################################################################# # Makefile for building: ../svn-all-fast-export -# Generated by qmake (2.01a) (Qt 4.8.7) on: Tue Jan 10 11:40:19 2017 +# Generated by qmake (3.1) (Qt 5.12.2) # Project: src.pro # Template: app -# Command: /usr/local/bin/qmake-qt4 -o Makefile src.pro +# Command: /usr/local/lib/qt5/bin/qmake -o Makefile src.pro ############################################################################# +MAKEFILE = Makefile + +EQ = = + ####### Compiler, tools and options CC = clang CXX = clang++ -DEFINES = -DVER="\"437beb493db3b1cb532ca5807264cd4403a44157\"" -DQT_NO_DEBUG -DQT_CORE_LIB -DQT_SHARED -CFLAGS = -pipe -O2 -Wall -W -pthread -D_THREAD_SAFE $(DEFINES) -CXXFLAGS = -pipe -O2 -Wall -W -pthread -D_THREAD_SAFE $(DEFINES) -INCPATH = -I/usr/local/share/qt4/mkspecs/freebsd-clang -I. -I/usr/local/include/qt4/QtCore -I/usr/local/include/qt4 -I. -I/usr/include/subversion-1 -I/usr/local/include/subversion-1 -I/usr/include/apr-1.0 -I/usr/include/apr-1 -I/usr/local/include/apr-1 -I. -I/usr/local/include/qt4 -I/usr/local/include -LINK = clang++ -LFLAGS = -Wl,-O1 -pthread -Wl,-rpath,/usr/local/lib/qt4 -LIBS = $(SUBLIBS) -L/usr/local/lib/qt4 -L/usr/local/lib -lsvn_fs-1 -lsvn_repos-1 -lapr-1 -lsvn_subr-1 -lQtCore -L/usr/local/lib/qt4 -L/usr/local/lib -AR = ar cqs -RANLIB = -QMAKE = /usr/local/bin/qmake-qt4 -TAR = tar -cf -COMPRESS = gzip -9f +DEFINES = -DVER="\"e9699437cad516b3b9494443f3132c15b8ef4485\"" -DQT_NO_DEBUG -DQT_CORE_LIB +CFLAGS = -pipe -O2 -Wall -W -pthread -fPIC $(DEFINES) +CXXFLAGS = -pipe -O2 -Wall -W -pthread -fPIC $(DEFINES) +INCPATH = -I. -I. -isystem /usr/include/subversion-1 -I/usr/local/include/subversion-1 -isystem /usr/include/apr-1.0 -isystem /usr/include/apr-1 -I/usr/local/include/apr-1 -I/usr/local/include/qt5 -I/usr/local/include/qt5/QtCore -I. -I/usr/local/include -I/usr/local/lib/qt5/mkspecs/freebsd-clang +QMAKE = /usr/local/lib/qt5/bin/qmake +DEL_FILE = rm -f +CHK_DIR_EXISTS= test -d +MKDIR = mkdir -p COPY = cp -f -SED = sed COPY_FILE = cp -f COPY_DIR = cp -f -R -STRIP = strip INSTALL_FILE = install -m 644 -p -INSTALL_DIR = $(COPY_DIR) INSTALL_PROGRAM = install -m 755 -p +INSTALL_DIR = cp -f -R +QINSTALL = /usr/local/lib/qt5/bin/qmake -install qinstall +QINSTALL_PROGRAM = /usr/local/lib/qt5/bin/qmake -install qinstall -exe DEL_FILE = rm -f SYMLINK = ln -f -s DEL_DIR = rmdir MOVE = mv -f -CHK_DIR_EXISTS= test -d -MKDIR = mkdir -p +TAR = tar -cf +COMPRESS = gzip -9f +DISTNAME = svn-all-fast-exporte9699437cad516b3b9494443f3132c15b8ef4485 +DISTDIR = /data/src/git_conv/svn2git/src/.tmp/svn-all-fast-exporte9699437cad516b3b9494443f3132c15b8ef4485 +LINK = clang++ +LFLAGS = -pthread -Wl,-rpath,/usr/local/lib/qt5 +LIBS = $(SUBLIBS) -lsvn_fs-1 -lsvn_repos-1 -lapr-1 -lsvn_subr-1 -L/usr/local/lib/qt5 -lQt5Core -lkvm -lprocstat -L/usr/local/lib -L/usr/local/lib +AR = ar cqs +RANLIB = +SED = sed +STRIP = strip ####### Output directory OBJECTS_DIR = ./ ####### Files SOURCES = ruleparser.cpp \ repository.cpp \ svn.cpp \ main.cpp \ CommandLineParser.cpp OBJECTS = ruleparser.o \ repository.o \ svn.o \ main.o \ CommandLineParser.o -DIST = /usr/local/share/qt4/mkspecs/common/unix.conf \ - /usr/local/share/qt4/mkspecs/common/freebsd.conf \ - /usr/local/share/qt4/mkspecs/common/gcc-base.conf \ - /usr/local/share/qt4/mkspecs/common/gcc-base-unix.conf \ - /usr/local/share/qt4/mkspecs/common/clang.conf \ - /usr/local/share/qt4/mkspecs/common/clang-unix.conf \ - /usr/local/share/qt4/mkspecs/qconfig.pri \ - /usr/local/share/qt4/mkspecs/features/qt_functions.prf \ - /usr/local/share/qt4/mkspecs/features/qt_config.prf \ - /usr/local/share/qt4/mkspecs/features/exclusive_builds.prf \ - /usr/local/share/qt4/mkspecs/features/default_pre.prf \ - /usr/local/share/qt4/mkspecs/features/release.prf \ - /usr/local/share/qt4/mkspecs/features/default_post.prf \ - /usr/local/share/qt4/mkspecs/features/shared.prf \ - /usr/local/share/qt4/mkspecs/features/unix/gdb_dwarf_index.prf \ - /usr/local/share/qt4/mkspecs/features/warn_on.prf \ - /usr/local/share/qt4/mkspecs/features/qt.prf \ - /usr/local/share/qt4/mkspecs/features/unix/thread.prf \ - /usr/local/share/qt4/mkspecs/features/moc.prf \ - /usr/local/share/qt4/mkspecs/features/resources.prf \ - /usr/local/share/qt4/mkspecs/features/uic.prf \ - /usr/local/share/qt4/mkspecs/features/yacc.prf \ - /usr/local/share/qt4/mkspecs/features/lex.prf \ - /usr/local/share/qt4/mkspecs/features/include_source_dir.prf \ - src.pro +DIST = /usr/local/lib/qt5/mkspecs/features/spec_pre.prf \ + /usr/local/lib/qt5/mkspecs/common/unix.conf \ + /usr/local/lib/qt5/mkspecs/common/bsd/bsd.conf \ + /usr/local/lib/qt5/mkspecs/common/sanitize.conf \ + /usr/local/lib/qt5/mkspecs/common/gcc-base.conf \ + /usr/local/lib/qt5/mkspecs/common/gcc-base-unix.conf \ + /usr/local/lib/qt5/mkspecs/common/clang.conf \ + /usr/local/lib/qt5/mkspecs/qconfig.pri \ + /usr/local/lib/qt5/mkspecs/modules/qt_config_core.pri \ + /usr/local/lib/qt5/mkspecs/modules/qt_lib_core.pri \ + /usr/local/lib/qt5/mkspecs/modules/qt_lib_core_private.pri \ + /usr/local/lib/qt5/mkspecs/features/qt_functions.prf \ + /usr/local/lib/qt5/mkspecs/features/qt_config.prf \ + /usr/local/lib/qt5/mkspecs/freebsd-clang/qmake.conf \ + /usr/local/lib/qt5/mkspecs/features/spec_post.prf \ + /usr/local/lib/qt5/mkspecs/features/exclusive_builds.prf \ + /usr/local/lib/qt5/mkspecs/features/toolchain.prf \ + /usr/local/lib/qt5/mkspecs/features/default_pre.prf \ + /usr/local/lib/qt5/mkspecs/features/resolve_config.prf \ + /usr/local/lib/qt5/mkspecs/features/default_post.prf \ + /usr/local/lib/qt5/mkspecs/features/warn_on.prf \ + /usr/local/lib/qt5/mkspecs/features/qt.prf \ + /usr/local/lib/qt5/mkspecs/features/resources.prf \ + /usr/local/lib/qt5/mkspecs/features/moc.prf \ + /usr/local/lib/qt5/mkspecs/features/unix/thread.prf \ + /usr/local/lib/qt5/mkspecs/features/qmake_use.prf \ + /usr/local/lib/qt5/mkspecs/features/file_copies.prf \ + /usr/local/lib/qt5/mkspecs/features/testcase_targets.prf \ + /usr/local/lib/qt5/mkspecs/features/exceptions.prf \ + /usr/local/lib/qt5/mkspecs/features/yacc.prf \ + /usr/local/lib/qt5/mkspecs/features/lex.prf \ + src.pro ruleparser.h \ + repository.h \ + svn.h \ + CommandLineParser.h ruleparser.cpp \ + repository.cpp \ + svn.cpp \ + main.cpp \ + CommandLineParser.cpp QMAKE_TARGET = svn-all-fast-export DESTDIR = ../ TARGET = ../svn-all-fast-export -first: all -####### Implicit rules -.SUFFIXES: .o .c .cpp .cc .cxx .C - -.cpp.o: - $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<" - -.cc.o: - $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<" - -.cxx.o: - $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<" - -.C.o: - $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<" - -.c.o: - $(CC) -c $(CFLAGS) $(INCPATH) -o "$@" "$<" - +first: all ####### Build rules -all: Makefile $(TARGET) - -$(TARGET): $(OBJECTS) - @$(CHK_DIR_EXISTS) ../ || $(MKDIR) ../ +../svn-all-fast-export: $(OBJECTS) + @test -d ../ || mkdir -p ../ $(LINK) $(LFLAGS) -o $(TARGET) $(OBJECTS) $(OBJCOMP) $(LIBS) -Makefile: src.pro /usr/local/share/qt4/mkspecs/freebsd-clang/qmake.conf /usr/local/share/qt4/mkspecs/common/unix.conf \ - /usr/local/share/qt4/mkspecs/common/freebsd.conf \ - /usr/local/share/qt4/mkspecs/common/gcc-base.conf \ - /usr/local/share/qt4/mkspecs/common/gcc-base-unix.conf \ - /usr/local/share/qt4/mkspecs/common/clang.conf \ - /usr/local/share/qt4/mkspecs/common/clang-unix.conf \ - /usr/local/share/qt4/mkspecs/qconfig.pri \ - /usr/local/share/qt4/mkspecs/features/qt_functions.prf \ - /usr/local/share/qt4/mkspecs/features/qt_config.prf \ - /usr/local/share/qt4/mkspecs/features/exclusive_builds.prf \ - /usr/local/share/qt4/mkspecs/features/default_pre.prf \ - /usr/local/share/qt4/mkspecs/features/release.prf \ - /usr/local/share/qt4/mkspecs/features/default_post.prf \ - /usr/local/share/qt4/mkspecs/features/shared.prf \ - /usr/local/share/qt4/mkspecs/features/unix/gdb_dwarf_index.prf \ - /usr/local/share/qt4/mkspecs/features/warn_on.prf \ - /usr/local/share/qt4/mkspecs/features/qt.prf \ - /usr/local/share/qt4/mkspecs/features/unix/thread.prf \ - /usr/local/share/qt4/mkspecs/features/moc.prf \ - /usr/local/share/qt4/mkspecs/features/resources.prf \ - /usr/local/share/qt4/mkspecs/features/uic.prf \ - /usr/local/share/qt4/mkspecs/features/yacc.prf \ - /usr/local/share/qt4/mkspecs/features/lex.prf \ - /usr/local/share/qt4/mkspecs/features/include_source_dir.prf \ - /usr/local/lib/qt4/libQtCore.prl +Makefile: src.pro /usr/local/lib/qt5/mkspecs/freebsd-clang/qmake.conf /usr/local/lib/qt5/mkspecs/features/spec_pre.prf \ + /usr/local/lib/qt5/mkspecs/common/unix.conf \ + /usr/local/lib/qt5/mkspecs/common/bsd/bsd.conf \ + /usr/local/lib/qt5/mkspecs/common/sanitize.conf \ + /usr/local/lib/qt5/mkspecs/common/gcc-base.conf \ + /usr/local/lib/qt5/mkspecs/common/gcc-base-unix.conf \ + /usr/local/lib/qt5/mkspecs/common/clang.conf \ + /usr/local/lib/qt5/mkspecs/qconfig.pri \ + /usr/local/lib/qt5/mkspecs/modules/qt_config_core.pri \ + /usr/local/lib/qt5/mkspecs/modules/qt_lib_core.pri \ + /usr/local/lib/qt5/mkspecs/modules/qt_lib_core_private.pri \ + /usr/local/lib/qt5/mkspecs/features/qt_functions.prf \ + /usr/local/lib/qt5/mkspecs/features/qt_config.prf \ + /usr/local/lib/qt5/mkspecs/freebsd-clang/qmake.conf \ + /usr/local/lib/qt5/mkspecs/features/spec_post.prf \ + /usr/local/lib/qt5/mkspecs/features/exclusive_builds.prf \ + /usr/local/lib/qt5/mkspecs/features/toolchain.prf \ + /usr/local/lib/qt5/mkspecs/features/default_pre.prf \ + /usr/local/lib/qt5/mkspecs/features/resolve_config.prf \ + /usr/local/lib/qt5/mkspecs/features/default_post.prf \ + /usr/local/lib/qt5/mkspecs/features/warn_on.prf \ + /usr/local/lib/qt5/mkspecs/features/qt.prf \ + /usr/local/lib/qt5/mkspecs/features/resources.prf \ + /usr/local/lib/qt5/mkspecs/features/moc.prf \ + /usr/local/lib/qt5/mkspecs/features/unix/thread.prf \ + /usr/local/lib/qt5/mkspecs/features/qmake_use.prf \ + /usr/local/lib/qt5/mkspecs/features/file_copies.prf \ + /usr/local/lib/qt5/mkspecs/features/testcase_targets.prf \ + /usr/local/lib/qt5/mkspecs/features/exceptions.prf \ + /usr/local/lib/qt5/mkspecs/features/yacc.prf \ + /usr/local/lib/qt5/mkspecs/features/lex.prf \ + src.pro \ + /usr/local/lib/qt5/libQt5Core.prl $(QMAKE) -o Makefile src.pro -/usr/local/share/qt4/mkspecs/common/unix.conf: -/usr/local/share/qt4/mkspecs/common/freebsd.conf: -/usr/local/share/qt4/mkspecs/common/gcc-base.conf: -/usr/local/share/qt4/mkspecs/common/gcc-base-unix.conf: -/usr/local/share/qt4/mkspecs/common/clang.conf: -/usr/local/share/qt4/mkspecs/common/clang-unix.conf: -/usr/local/share/qt4/mkspecs/qconfig.pri: -/usr/local/share/qt4/mkspecs/features/qt_functions.prf: -/usr/local/share/qt4/mkspecs/features/qt_config.prf: -/usr/local/share/qt4/mkspecs/features/exclusive_builds.prf: -/usr/local/share/qt4/mkspecs/features/default_pre.prf: -/usr/local/share/qt4/mkspecs/features/release.prf: -/usr/local/share/qt4/mkspecs/features/default_post.prf: -/usr/local/share/qt4/mkspecs/features/shared.prf: -/usr/local/share/qt4/mkspecs/features/unix/gdb_dwarf_index.prf: -/usr/local/share/qt4/mkspecs/features/warn_on.prf: -/usr/local/share/qt4/mkspecs/features/qt.prf: -/usr/local/share/qt4/mkspecs/features/unix/thread.prf: -/usr/local/share/qt4/mkspecs/features/moc.prf: -/usr/local/share/qt4/mkspecs/features/resources.prf: -/usr/local/share/qt4/mkspecs/features/uic.prf: -/usr/local/share/qt4/mkspecs/features/yacc.prf: -/usr/local/share/qt4/mkspecs/features/lex.prf: -/usr/local/share/qt4/mkspecs/features/include_source_dir.prf: -/usr/local/lib/qt4/libQtCore.prl: -qmake: FORCE +/usr/local/lib/qt5/mkspecs/features/spec_pre.prf: +/usr/local/lib/qt5/mkspecs/common/unix.conf: +/usr/local/lib/qt5/mkspecs/common/bsd/bsd.conf: +/usr/local/lib/qt5/mkspecs/common/sanitize.conf: +/usr/local/lib/qt5/mkspecs/common/gcc-base.conf: +/usr/local/lib/qt5/mkspecs/common/gcc-base-unix.conf: +/usr/local/lib/qt5/mkspecs/common/clang.conf: +/usr/local/lib/qt5/mkspecs/qconfig.pri: +/usr/local/lib/qt5/mkspecs/modules/qt_config_core.pri: +/usr/local/lib/qt5/mkspecs/modules/qt_lib_core.pri: +/usr/local/lib/qt5/mkspecs/modules/qt_lib_core_private.pri: +/usr/local/lib/qt5/mkspecs/features/qt_functions.prf: +/usr/local/lib/qt5/mkspecs/features/qt_config.prf: +/usr/local/lib/qt5/mkspecs/freebsd-clang/qmake.conf: +/usr/local/lib/qt5/mkspecs/features/spec_post.prf: +/usr/local/lib/qt5/mkspecs/features/exclusive_builds.prf: +/usr/local/lib/qt5/mkspecs/features/toolchain.prf: +/usr/local/lib/qt5/mkspecs/features/default_pre.prf: +/usr/local/lib/qt5/mkspecs/features/resolve_config.prf: +/usr/local/lib/qt5/mkspecs/features/default_post.prf: +/usr/local/lib/qt5/mkspecs/features/warn_on.prf: +/usr/local/lib/qt5/mkspecs/features/qt.prf: +/usr/local/lib/qt5/mkspecs/features/resources.prf: +/usr/local/lib/qt5/mkspecs/features/moc.prf: +/usr/local/lib/qt5/mkspecs/features/unix/thread.prf: +/usr/local/lib/qt5/mkspecs/features/qmake_use.prf: +/usr/local/lib/qt5/mkspecs/features/file_copies.prf: +/usr/local/lib/qt5/mkspecs/features/testcase_targets.prf: +/usr/local/lib/qt5/mkspecs/features/exceptions.prf: +/usr/local/lib/qt5/mkspecs/features/yacc.prf: +/usr/local/lib/qt5/mkspecs/features/lex.prf: +src.pro: +/usr/local/lib/qt5/libQt5Core.prl: +qmake: FORCE @$(QMAKE) -o Makefile src.pro -dist: - @$(CHK_DIR_EXISTS) .tmp/svn-all-fast-export437beb493db3b1cb532ca5807264cd4403a44157 || $(MKDIR) .tmp/svn-all-fast-export437beb493db3b1cb532ca5807264cd4403a44157 - $(COPY_FILE) --parents $(SOURCES) $(DIST) .tmp/svn-all-fast-export437beb493db3b1cb532ca5807264cd4403a44157/ && $(COPY_FILE) --parents ruleparser.h repository.h svn.h CommandLineParser.h .tmp/svn-all-fast-export437beb493db3b1cb532ca5807264cd4403a44157/ && $(COPY_FILE) --parents ruleparser.cpp repository.cpp svn.cpp main.cpp CommandLineParser.cpp .tmp/svn-all-fast-export437beb493db3b1cb532ca5807264cd4403a44157/ && (cd `dirname .tmp/svn-all-fast-export437beb493db3b1cb532ca5807264cd4403a44157` && $(TAR) svn-all-fast-export437beb493db3b1cb532ca5807264cd4403a44157.tar svn-all-fast-export437beb493db3b1cb532ca5807264cd4403a44157 && $(COMPRESS) svn-all-fast-export437beb493db3b1cb532ca5807264cd4403a44157.tar) && $(MOVE) `dirname .tmp/svn-all-fast-export437beb493db3b1cb532ca5807264cd4403a44157`/svn-all-fast-export437beb493db3b1cb532ca5807264cd4403a44157.tar.gz . && $(DEL_FILE) -r .tmp/svn-all-fast-export437beb493db3b1cb532ca5807264cd4403a44157 +qmake_all: FORCE -clean:compiler_clean +all: Makefile ../svn-all-fast-export + +dist: distdir FORCE + (cd `dirname $(DISTDIR)` && $(TAR) $(DISTNAME).tar $(DISTNAME) && $(COMPRESS) $(DISTNAME).tar) && $(MOVE) `dirname $(DISTDIR)`/$(DISTNAME).tar.gz . && $(DEL_FILE) -r $(DISTDIR) + +distdir: FORCE + @test -d $(DISTDIR) || mkdir -p $(DISTDIR) + $(COPY_FILE) --parents $(DIST) $(DISTDIR)/ + $(COPY_FILE) --parents /usr/local/lib/qt5/mkspecs/features/data/dummy.cpp $(DISTDIR)/ + $(COPY_FILE) --parents ruleparser.h repository.h svn.h CommandLineParser.h $(DISTDIR)/ + $(COPY_FILE) --parents ruleparser.cpp repository.cpp svn.cpp main.cpp CommandLineParser.cpp $(DISTDIR)/ + + +clean: compiler_clean -$(DEL_FILE) $(OBJECTS) -$(DEL_FILE) *~ core *.core -####### Sub-libraries - -distclean: clean +distclean: clean -$(DEL_FILE) $(TARGET) + -$(DEL_FILE) .qmake.stash -$(DEL_FILE) Makefile +####### Sub-libraries + +mocclean: compiler_moc_header_clean compiler_moc_objc_header_clean compiler_moc_source_clean + +mocables: compiler_moc_header_make_all compiler_moc_objc_header_make_all compiler_moc_source_make_all + check: first -mocclean: compiler_moc_header_clean compiler_moc_source_clean +benchmark: first -mocables: compiler_moc_header_make_all compiler_moc_source_make_all +compiler_rcc_make_all: +compiler_rcc_clean: +compiler_moc_predefs_make_all: moc_predefs.h +compiler_moc_predefs_clean: + -$(DEL_FILE) moc_predefs.h +moc_predefs.h: /usr/local/lib/qt5/mkspecs/features/data/dummy.cpp + clang++ -pipe -O2 -Wall -W -dM -E -o moc_predefs.h /usr/local/lib/qt5/mkspecs/features/data/dummy.cpp compiler_moc_header_make_all: compiler_moc_header_clean: -compiler_rcc_make_all: -compiler_rcc_clean: -compiler_image_collection_make_all: qmake_image_collection.cpp -compiler_image_collection_clean: - -$(DEL_FILE) qmake_image_collection.cpp +compiler_moc_objc_header_make_all: +compiler_moc_objc_header_clean: compiler_moc_source_make_all: compiler_moc_source_clean: -compiler_uic_make_all: -compiler_uic_clean: compiler_yacc_decl_make_all: compiler_yacc_decl_clean: compiler_yacc_impl_make_all: compiler_yacc_impl_clean: compiler_lex_make_all: compiler_lex_clean: -compiler_clean: +compiler_clean: compiler_moc_predefs_clean ####### Compile -ruleparser.o: ruleparser.cpp ruleparser.h \ +ruleparser.o: ruleparser.cpp /usr/local/include/qt5/QtCore/QTextStream \ + /usr/local/include/qt5/QtCore/qtextstream.h \ + /usr/local/include/qt5/QtCore/qiodevice.h \ + /usr/local/include/qt5/QtCore/qglobal.h \ + /usr/local/include/qt5/QtCore/qconfig-bootstrapped.h \ + /usr/local/include/qt5/QtCore/qconfig.h \ + /usr/local/include/qt5/QtCore/qconfig-modules.h \ + /usr/local/include/qt5/QtCore/modules/qconfig-core.h \ + /usr/local/include/qt5/QtCore/qtcore-config.h \ + /usr/local/include/qt5/QtCore/qsystemdetection.h \ + /usr/local/include/qt5/QtCore/qprocessordetection.h \ + /usr/local/include/qt5/QtCore/qcompilerdetection.h \ + /usr/local/include/qt5/QtCore/qtypeinfo.h \ + /usr/local/include/qt5/QtCore/qsysinfo.h \ + /usr/local/include/qt5/QtCore/qlogging.h \ + /usr/local/include/qt5/QtCore/qflags.h \ + /usr/local/include/qt5/QtCore/qatomic.h \ + /usr/local/include/qt5/QtCore/qbasicatomic.h \ + /usr/local/include/qt5/QtCore/qatomic_bootstrap.h \ + /usr/local/include/qt5/QtCore/qgenericatomic.h \ + /usr/local/include/qt5/QtCore/qatomic_cxx11.h \ + /usr/local/include/qt5/QtCore/qatomic_msvc.h \ + /usr/local/include/qt5/QtCore/qglobalstatic.h \ + /usr/local/include/qt5/QtCore/qmutex.h \ + /usr/local/include/qt5/QtCore/qnumeric.h \ + /usr/local/include/qt5/QtCore/qversiontagging.h \ + /usr/local/include/qt5/QtCore/qobject.h \ + /usr/local/include/qt5/QtCore/qobjectdefs.h \ + /usr/local/include/qt5/QtCore/qnamespace.h \ + /usr/local/include/qt5/QtCore/qobjectdefs_impl.h \ + /usr/local/include/qt5/QtCore/qstring.h \ + /usr/local/include/qt5/QtCore/qchar.h \ + /usr/local/include/qt5/QtCore/qbytearray.h \ + /usr/local/include/qt5/QtCore/qrefcount.h \ + /usr/local/include/qt5/QtCore/qarraydata.h \ + /usr/local/include/qt5/QtCore/qstringliteral.h \ + /usr/local/include/qt5/QtCore/qstringalgorithms.h \ + /usr/local/include/qt5/QtCore/qstringview.h \ + /usr/local/include/qt5/QtCore/qstringbuilder.h \ + /usr/local/include/qt5/QtCore/qlist.h \ + /usr/local/include/qt5/QtCore/qalgorithms.h \ + /usr/local/include/qt5/QtCore/qiterator.h \ + /usr/local/include/qt5/QtCore/qhashfunctions.h \ + /usr/local/include/qt5/QtCore/qpair.h \ + /usr/local/include/qt5/QtCore/qbytearraylist.h \ + /usr/local/include/qt5/QtCore/qstringlist.h \ + /usr/local/include/qt5/QtCore/qregexp.h \ + /usr/local/include/qt5/QtCore/qstringmatcher.h \ + /usr/local/include/qt5/QtCore/qcoreevent.h \ + /usr/local/include/qt5/QtCore/qscopedpointer.h \ + /usr/local/include/qt5/QtCore/qmetatype.h \ + /usr/local/include/qt5/QtCore/qvarlengtharray.h \ + /usr/local/include/qt5/QtCore/qcontainerfwd.h \ + /usr/local/include/qt5/QtCore/qobject_impl.h \ + /usr/local/include/qt5/QtCore/qlocale.h \ + /usr/local/include/qt5/QtCore/qvariant.h \ + /usr/local/include/qt5/QtCore/qmap.h \ + /usr/local/include/qt5/QtCore/qdebug.h \ + /usr/local/include/qt5/QtCore/qhash.h \ + /usr/local/include/qt5/QtCore/qvector.h \ + /usr/local/include/qt5/QtCore/qpoint.h \ + /usr/local/include/qt5/QtCore/qset.h \ + /usr/local/include/qt5/QtCore/qcontiguouscache.h \ + /usr/local/include/qt5/QtCore/qsharedpointer.h \ + /usr/local/include/qt5/QtCore/qshareddata.h \ + /usr/local/include/qt5/QtCore/qsharedpointer_impl.h \ + /usr/local/include/qt5/QtCore/QList \ + /usr/local/include/qt5/QtCore/QFile \ + /usr/local/include/qt5/QtCore/qfile.h \ + /usr/local/include/qt5/QtCore/qfiledevice.h \ + /usr/local/include/qt5/QtCore/QDebug \ + ruleparser.h \ + /usr/local/include/qt5/QtCore/QMap \ + /usr/local/include/qt5/QtCore/QRegExp \ + /usr/local/include/qt5/QtCore/QString \ + /usr/local/include/qt5/QtCore/QStringList \ + /usr/local/include/qt5/QtCore/QStringBuilder \ CommandLineParser.h $(CXX) -c $(CXXFLAGS) $(INCPATH) -o ruleparser.o ruleparser.cpp repository.o: repository.cpp repository.h \ + /usr/local/include/qt5/QtCore/QHash \ + /usr/local/include/qt5/QtCore/qhash.h \ + /usr/local/include/qt5/QtCore/qchar.h \ + /usr/local/include/qt5/QtCore/qglobal.h \ + /usr/local/include/qt5/QtCore/qconfig-bootstrapped.h \ + /usr/local/include/qt5/QtCore/qconfig.h \ + /usr/local/include/qt5/QtCore/qconfig-modules.h \ + /usr/local/include/qt5/QtCore/modules/qconfig-core.h \ + /usr/local/include/qt5/QtCore/qtcore-config.h \ + /usr/local/include/qt5/QtCore/qsystemdetection.h \ + /usr/local/include/qt5/QtCore/qprocessordetection.h \ + /usr/local/include/qt5/QtCore/qcompilerdetection.h \ + /usr/local/include/qt5/QtCore/qtypeinfo.h \ + /usr/local/include/qt5/QtCore/qsysinfo.h \ + /usr/local/include/qt5/QtCore/qlogging.h \ + /usr/local/include/qt5/QtCore/qflags.h \ + /usr/local/include/qt5/QtCore/qatomic.h \ + /usr/local/include/qt5/QtCore/qbasicatomic.h \ + /usr/local/include/qt5/QtCore/qatomic_bootstrap.h \ + /usr/local/include/qt5/QtCore/qgenericatomic.h \ + /usr/local/include/qt5/QtCore/qatomic_cxx11.h \ + /usr/local/include/qt5/QtCore/qatomic_msvc.h \ + /usr/local/include/qt5/QtCore/qglobalstatic.h \ + /usr/local/include/qt5/QtCore/qmutex.h \ + /usr/local/include/qt5/QtCore/qnumeric.h \ + /usr/local/include/qt5/QtCore/qversiontagging.h \ + /usr/local/include/qt5/QtCore/qiterator.h \ + /usr/local/include/qt5/QtCore/qlist.h \ + /usr/local/include/qt5/QtCore/qalgorithms.h \ + /usr/local/include/qt5/QtCore/qrefcount.h \ + /usr/local/include/qt5/QtCore/qarraydata.h \ + /usr/local/include/qt5/QtCore/qhashfunctions.h \ + /usr/local/include/qt5/QtCore/qstring.h \ + /usr/local/include/qt5/QtCore/qbytearray.h \ + /usr/local/include/qt5/QtCore/qnamespace.h \ + /usr/local/include/qt5/QtCore/qstringliteral.h \ + /usr/local/include/qt5/QtCore/qstringalgorithms.h \ + /usr/local/include/qt5/QtCore/qstringview.h \ + /usr/local/include/qt5/QtCore/qstringbuilder.h \ + /usr/local/include/qt5/QtCore/qpair.h \ + /usr/local/include/qt5/QtCore/qbytearraylist.h \ + /usr/local/include/qt5/QtCore/qstringlist.h \ + /usr/local/include/qt5/QtCore/qregexp.h \ + /usr/local/include/qt5/QtCore/qstringmatcher.h \ + /usr/local/include/qt5/QtCore/QProcess \ + /usr/local/include/qt5/QtCore/qprocess.h \ + /usr/local/include/qt5/QtCore/qiodevice.h \ + /usr/local/include/qt5/QtCore/qobject.h \ + /usr/local/include/qt5/QtCore/qobjectdefs.h \ + /usr/local/include/qt5/QtCore/qobjectdefs_impl.h \ + /usr/local/include/qt5/QtCore/qcoreevent.h \ + /usr/local/include/qt5/QtCore/qscopedpointer.h \ + /usr/local/include/qt5/QtCore/qmetatype.h \ + /usr/local/include/qt5/QtCore/qvarlengtharray.h \ + /usr/local/include/qt5/QtCore/qcontainerfwd.h \ + /usr/local/include/qt5/QtCore/qobject_impl.h \ + /usr/local/include/qt5/QtCore/qshareddata.h \ + /usr/local/include/qt5/QtCore/QVector \ + /usr/local/include/qt5/QtCore/qvector.h \ + /usr/local/include/qt5/QtCore/qpoint.h \ + /usr/local/include/qt5/QtCore/QFile \ + /usr/local/include/qt5/QtCore/qfile.h \ + /usr/local/include/qt5/QtCore/qfiledevice.h \ ruleparser.h \ - CommandLineParser.h + /usr/local/include/qt5/QtCore/QList \ + /usr/local/include/qt5/QtCore/QMap \ + /usr/local/include/qt5/QtCore/qmap.h \ + /usr/local/include/qt5/QtCore/qdebug.h \ + /usr/local/include/qt5/QtCore/qtextstream.h \ + /usr/local/include/qt5/QtCore/qlocale.h \ + /usr/local/include/qt5/QtCore/qvariant.h \ + /usr/local/include/qt5/QtCore/qset.h \ + /usr/local/include/qt5/QtCore/qcontiguouscache.h \ + /usr/local/include/qt5/QtCore/qsharedpointer.h \ + /usr/local/include/qt5/QtCore/qsharedpointer_impl.h \ + /usr/local/include/qt5/QtCore/QRegExp \ + /usr/local/include/qt5/QtCore/QString \ + /usr/local/include/qt5/QtCore/QStringList \ + /usr/local/include/qt5/QtCore/QStringBuilder \ + CommandLineParser.h \ + /usr/local/include/qt5/QtCore/QTextStream \ + /usr/local/include/qt5/QtCore/QDataStream \ + /usr/local/include/qt5/QtCore/qdatastream.h \ + /usr/local/include/qt5/QtCore/QDebug \ + /usr/local/include/qt5/QtCore/QDir \ + /usr/local/include/qt5/QtCore/qdir.h \ + /usr/local/include/qt5/QtCore/qfileinfo.h \ + /usr/local/include/qt5/QtCore/QLinkedList \ + /usr/local/include/qt5/QtCore/qlinkedlist.h $(CXX) -c $(CXXFLAGS) $(INCPATH) -o repository.o repository.cpp svn.o: svn.cpp svn.h \ + /usr/local/include/qt5/QtCore/QHash \ + /usr/local/include/qt5/QtCore/qhash.h \ + /usr/local/include/qt5/QtCore/qchar.h \ + /usr/local/include/qt5/QtCore/qglobal.h \ + /usr/local/include/qt5/QtCore/qconfig-bootstrapped.h \ + /usr/local/include/qt5/QtCore/qconfig.h \ + /usr/local/include/qt5/QtCore/qconfig-modules.h \ + /usr/local/include/qt5/QtCore/modules/qconfig-core.h \ + /usr/local/include/qt5/QtCore/qtcore-config.h \ + /usr/local/include/qt5/QtCore/qsystemdetection.h \ + /usr/local/include/qt5/QtCore/qprocessordetection.h \ + /usr/local/include/qt5/QtCore/qcompilerdetection.h \ + /usr/local/include/qt5/QtCore/qtypeinfo.h \ + /usr/local/include/qt5/QtCore/qsysinfo.h \ + /usr/local/include/qt5/QtCore/qlogging.h \ + /usr/local/include/qt5/QtCore/qflags.h \ + /usr/local/include/qt5/QtCore/qatomic.h \ + /usr/local/include/qt5/QtCore/qbasicatomic.h \ + /usr/local/include/qt5/QtCore/qatomic_bootstrap.h \ + /usr/local/include/qt5/QtCore/qgenericatomic.h \ + /usr/local/include/qt5/QtCore/qatomic_cxx11.h \ + /usr/local/include/qt5/QtCore/qatomic_msvc.h \ + /usr/local/include/qt5/QtCore/qglobalstatic.h \ + /usr/local/include/qt5/QtCore/qmutex.h \ + /usr/local/include/qt5/QtCore/qnumeric.h \ + /usr/local/include/qt5/QtCore/qversiontagging.h \ + /usr/local/include/qt5/QtCore/qiterator.h \ + /usr/local/include/qt5/QtCore/qlist.h \ + /usr/local/include/qt5/QtCore/qalgorithms.h \ + /usr/local/include/qt5/QtCore/qrefcount.h \ + /usr/local/include/qt5/QtCore/qarraydata.h \ + /usr/local/include/qt5/QtCore/qhashfunctions.h \ + /usr/local/include/qt5/QtCore/qstring.h \ + /usr/local/include/qt5/QtCore/qbytearray.h \ + /usr/local/include/qt5/QtCore/qnamespace.h \ + /usr/local/include/qt5/QtCore/qstringliteral.h \ + /usr/local/include/qt5/QtCore/qstringalgorithms.h \ + /usr/local/include/qt5/QtCore/qstringview.h \ + /usr/local/include/qt5/QtCore/qstringbuilder.h \ + /usr/local/include/qt5/QtCore/qpair.h \ + /usr/local/include/qt5/QtCore/qbytearraylist.h \ + /usr/local/include/qt5/QtCore/qstringlist.h \ + /usr/local/include/qt5/QtCore/qregexp.h \ + /usr/local/include/qt5/QtCore/qstringmatcher.h \ + /usr/local/include/qt5/QtCore/QList \ ruleparser.h \ + /usr/local/include/qt5/QtCore/QMap \ + /usr/local/include/qt5/QtCore/qmap.h \ + /usr/local/include/qt5/QtCore/qdebug.h \ + /usr/local/include/qt5/QtCore/qtextstream.h \ + /usr/local/include/qt5/QtCore/qiodevice.h \ + /usr/local/include/qt5/QtCore/qobject.h \ + /usr/local/include/qt5/QtCore/qobjectdefs.h \ + /usr/local/include/qt5/QtCore/qobjectdefs_impl.h \ + /usr/local/include/qt5/QtCore/qcoreevent.h \ + /usr/local/include/qt5/QtCore/qscopedpointer.h \ + /usr/local/include/qt5/QtCore/qmetatype.h \ + /usr/local/include/qt5/QtCore/qvarlengtharray.h \ + /usr/local/include/qt5/QtCore/qcontainerfwd.h \ + /usr/local/include/qt5/QtCore/qobject_impl.h \ + /usr/local/include/qt5/QtCore/qlocale.h \ + /usr/local/include/qt5/QtCore/qvariant.h \ + /usr/local/include/qt5/QtCore/qshareddata.h \ + /usr/local/include/qt5/QtCore/qvector.h \ + /usr/local/include/qt5/QtCore/qpoint.h \ + /usr/local/include/qt5/QtCore/qset.h \ + /usr/local/include/qt5/QtCore/qcontiguouscache.h \ + /usr/local/include/qt5/QtCore/qsharedpointer.h \ + /usr/local/include/qt5/QtCore/qsharedpointer_impl.h \ + /usr/local/include/qt5/QtCore/QRegExp \ + /usr/local/include/qt5/QtCore/QString \ + /usr/local/include/qt5/QtCore/QStringList \ + /usr/local/include/qt5/QtCore/QStringBuilder \ CommandLineParser.h \ - repository.h + /usr/local/include/apr-1/apr_lib.h \ + /usr/local/include/apr-1/apr.h \ + /usr/local/include/apr-1/apr_errno.h \ + /usr/local/include/apr-1/apr_getopt.h \ + /usr/local/include/apr-1/apr_pools.h \ + /usr/local/include/apr-1/apr_general.h \ + /usr/local/include/apr-1/apr_want.h \ + /usr/local/include/apr-1/apr_allocator.h \ + /usr/local/include/apr-1/apr_thread_mutex.h \ + /usr/local/include/apr-1/apr_time.h \ + /usr/local/include/subversion-1/svn_fs.h \ + /usr/local/include/apr-1/apr_hash.h \ + /usr/local/include/apr-1/apr_tables.h \ + /usr/local/include/subversion-1/svn_types.h \ + /usr/local/include/subversion-1/svn_types_impl.h \ + /usr/local/include/apr-1/apr_version.h \ + /usr/local/include/apr-1/apr_strings.h \ + /usr/local/include/subversion-1/svn_error.h \ + /usr/local/include/subversion-1/svn_error_codes.h \ + /usr/local/include/subversion-1/svn_string.h \ + /usr/local/include/subversion-1/svn_delta.h \ + /usr/local/include/apr-1/apr_file_io.h \ + /usr/local/include/apr-1/apr_file_info.h \ + /usr/local/include/apr-1/apr_user.h \ + /usr/local/include/apr-1/apr_inherit.h \ + /usr/local/include/subversion-1/svn_io.h \ + /usr/local/include/apr-1/apr_thread_proc.h \ + /usr/local/include/apr-1/apr_perms_set.h \ + /usr/local/include/subversion-1/svn_checksum.h \ + /usr/local/include/subversion-1/svn_mergeinfo.h \ + /usr/local/include/subversion-1/svn_pools.h \ + /usr/local/include/subversion-1/svn_repos.h \ + /usr/local/include/subversion-1/svn_version.h \ + /usr/local/include/qt5/QtCore/QFile \ + /usr/local/include/qt5/QtCore/qfile.h \ + /usr/local/include/qt5/QtCore/qfiledevice.h \ + /usr/local/include/qt5/QtCore/QDebug \ + repository.h \ + /usr/local/include/qt5/QtCore/QProcess \ + /usr/local/include/qt5/QtCore/qprocess.h \ + /usr/local/include/qt5/QtCore/QVector $(CXX) -c $(CXXFLAGS) $(INCPATH) -o svn.o svn.cpp -main.o: main.cpp CommandLineParser.h \ +main.o: main.cpp /usr/local/include/qt5/QtCore/QCoreApplication \ + /usr/local/include/qt5/QtCore/qcoreapplication.h \ + /usr/local/include/qt5/QtCore/qglobal.h \ + /usr/local/include/qt5/QtCore/qconfig-bootstrapped.h \ + /usr/local/include/qt5/QtCore/qconfig.h \ + /usr/local/include/qt5/QtCore/qconfig-modules.h \ + /usr/local/include/qt5/QtCore/modules/qconfig-core.h \ + /usr/local/include/qt5/QtCore/qtcore-config.h \ + /usr/local/include/qt5/QtCore/qsystemdetection.h \ + /usr/local/include/qt5/QtCore/qprocessordetection.h \ + /usr/local/include/qt5/QtCore/qcompilerdetection.h \ + /usr/local/include/qt5/QtCore/qtypeinfo.h \ + /usr/local/include/qt5/QtCore/qsysinfo.h \ + /usr/local/include/qt5/QtCore/qlogging.h \ + /usr/local/include/qt5/QtCore/qflags.h \ + /usr/local/include/qt5/QtCore/qatomic.h \ + /usr/local/include/qt5/QtCore/qbasicatomic.h \ + /usr/local/include/qt5/QtCore/qatomic_bootstrap.h \ + /usr/local/include/qt5/QtCore/qgenericatomic.h \ + /usr/local/include/qt5/QtCore/qatomic_cxx11.h \ + /usr/local/include/qt5/QtCore/qatomic_msvc.h \ + /usr/local/include/qt5/QtCore/qglobalstatic.h \ + /usr/local/include/qt5/QtCore/qmutex.h \ + /usr/local/include/qt5/QtCore/qnumeric.h \ + /usr/local/include/qt5/QtCore/qversiontagging.h \ + /usr/local/include/qt5/QtCore/qstring.h \ + /usr/local/include/qt5/QtCore/qchar.h \ + /usr/local/include/qt5/QtCore/qbytearray.h \ + /usr/local/include/qt5/QtCore/qrefcount.h \ + /usr/local/include/qt5/QtCore/qnamespace.h \ + /usr/local/include/qt5/QtCore/qarraydata.h \ + /usr/local/include/qt5/QtCore/qstringliteral.h \ + /usr/local/include/qt5/QtCore/qstringalgorithms.h \ + /usr/local/include/qt5/QtCore/qstringview.h \ + /usr/local/include/qt5/QtCore/qstringbuilder.h \ + /usr/local/include/qt5/QtCore/qobject.h \ + /usr/local/include/qt5/QtCore/qobjectdefs.h \ + /usr/local/include/qt5/QtCore/qobjectdefs_impl.h \ + /usr/local/include/qt5/QtCore/qlist.h \ + /usr/local/include/qt5/QtCore/qalgorithms.h \ + /usr/local/include/qt5/QtCore/qiterator.h \ + /usr/local/include/qt5/QtCore/qhashfunctions.h \ + /usr/local/include/qt5/QtCore/qpair.h \ + /usr/local/include/qt5/QtCore/qbytearraylist.h \ + /usr/local/include/qt5/QtCore/qstringlist.h \ + /usr/local/include/qt5/QtCore/qregexp.h \ + /usr/local/include/qt5/QtCore/qstringmatcher.h \ + /usr/local/include/qt5/QtCore/qcoreevent.h \ + /usr/local/include/qt5/QtCore/qscopedpointer.h \ + /usr/local/include/qt5/QtCore/qmetatype.h \ + /usr/local/include/qt5/QtCore/qvarlengtharray.h \ + /usr/local/include/qt5/QtCore/qcontainerfwd.h \ + /usr/local/include/qt5/QtCore/qobject_impl.h \ + /usr/local/include/qt5/QtCore/qeventloop.h \ + /usr/local/include/qt5/QtCore/QFile \ + /usr/local/include/qt5/QtCore/qfile.h \ + /usr/local/include/qt5/QtCore/qfiledevice.h \ + /usr/local/include/qt5/QtCore/qiodevice.h \ + /usr/local/include/qt5/QtCore/QStringList \ + /usr/local/include/qt5/QtCore/QTextStream \ + /usr/local/include/qt5/QtCore/qtextstream.h \ + /usr/local/include/qt5/QtCore/qlocale.h \ + /usr/local/include/qt5/QtCore/qvariant.h \ + /usr/local/include/qt5/QtCore/qmap.h \ + /usr/local/include/qt5/QtCore/qdebug.h \ + /usr/local/include/qt5/QtCore/qhash.h \ + /usr/local/include/qt5/QtCore/qvector.h \ + /usr/local/include/qt5/QtCore/qpoint.h \ + /usr/local/include/qt5/QtCore/qset.h \ + /usr/local/include/qt5/QtCore/qcontiguouscache.h \ + /usr/local/include/qt5/QtCore/qsharedpointer.h \ + /usr/local/include/qt5/QtCore/qshareddata.h \ + /usr/local/include/qt5/QtCore/qsharedpointer_impl.h \ + /usr/local/include/qt5/QtCore/QDebug \ + CommandLineParser.h \ + /usr/local/include/qt5/QtCore/QString \ ruleparser.h \ + /usr/local/include/qt5/QtCore/QList \ + /usr/local/include/qt5/QtCore/QMap \ + /usr/local/include/qt5/QtCore/QRegExp \ + /usr/local/include/qt5/QtCore/QStringBuilder \ repository.h \ + /usr/local/include/qt5/QtCore/QHash \ + /usr/local/include/qt5/QtCore/QProcess \ + /usr/local/include/qt5/QtCore/qprocess.h \ + /usr/local/include/qt5/QtCore/QVector \ svn.h $(CXX) -c $(CXXFLAGS) $(INCPATH) -o main.o main.cpp -CommandLineParser.o: CommandLineParser.cpp CommandLineParser.h +CommandLineParser.o: CommandLineParser.cpp CommandLineParser.h \ + /usr/local/include/qt5/QtCore/QString \ + /usr/local/include/qt5/QtCore/qstring.h \ + /usr/local/include/qt5/QtCore/qchar.h \ + /usr/local/include/qt5/QtCore/qglobal.h \ + /usr/local/include/qt5/QtCore/qconfig-bootstrapped.h \ + /usr/local/include/qt5/QtCore/qconfig.h \ + /usr/local/include/qt5/QtCore/qconfig-modules.h \ + /usr/local/include/qt5/QtCore/modules/qconfig-core.h \ + /usr/local/include/qt5/QtCore/qtcore-config.h \ + /usr/local/include/qt5/QtCore/qsystemdetection.h \ + /usr/local/include/qt5/QtCore/qprocessordetection.h \ + /usr/local/include/qt5/QtCore/qcompilerdetection.h \ + /usr/local/include/qt5/QtCore/qtypeinfo.h \ + /usr/local/include/qt5/QtCore/qsysinfo.h \ + /usr/local/include/qt5/QtCore/qlogging.h \ + /usr/local/include/qt5/QtCore/qflags.h \ + /usr/local/include/qt5/QtCore/qatomic.h \ + /usr/local/include/qt5/QtCore/qbasicatomic.h \ + /usr/local/include/qt5/QtCore/qatomic_bootstrap.h \ + /usr/local/include/qt5/QtCore/qgenericatomic.h \ + /usr/local/include/qt5/QtCore/qatomic_cxx11.h \ + /usr/local/include/qt5/QtCore/qatomic_msvc.h \ + /usr/local/include/qt5/QtCore/qglobalstatic.h \ + /usr/local/include/qt5/QtCore/qmutex.h \ + /usr/local/include/qt5/QtCore/qnumeric.h \ + /usr/local/include/qt5/QtCore/qversiontagging.h \ + /usr/local/include/qt5/QtCore/qbytearray.h \ + /usr/local/include/qt5/QtCore/qrefcount.h \ + /usr/local/include/qt5/QtCore/qnamespace.h \ + /usr/local/include/qt5/QtCore/qarraydata.h \ + /usr/local/include/qt5/QtCore/qstringliteral.h \ + /usr/local/include/qt5/QtCore/qstringalgorithms.h \ + /usr/local/include/qt5/QtCore/qstringview.h \ + /usr/local/include/qt5/QtCore/qstringbuilder.h \ + /usr/local/include/qt5/QtCore/QDebug \ + /usr/local/include/qt5/QtCore/qdebug.h \ + /usr/local/include/qt5/QtCore/qalgorithms.h \ + /usr/local/include/qt5/QtCore/qhash.h \ + /usr/local/include/qt5/QtCore/qiterator.h \ + /usr/local/include/qt5/QtCore/qlist.h \ + /usr/local/include/qt5/QtCore/qhashfunctions.h \ + /usr/local/include/qt5/QtCore/qpair.h \ + /usr/local/include/qt5/QtCore/qbytearraylist.h \ + /usr/local/include/qt5/QtCore/qstringlist.h \ + /usr/local/include/qt5/QtCore/qregexp.h \ + /usr/local/include/qt5/QtCore/qstringmatcher.h \ + /usr/local/include/qt5/QtCore/qmap.h \ + /usr/local/include/qt5/QtCore/qtextstream.h \ + /usr/local/include/qt5/QtCore/qiodevice.h \ + /usr/local/include/qt5/QtCore/qobject.h \ + /usr/local/include/qt5/QtCore/qobjectdefs.h \ + /usr/local/include/qt5/QtCore/qobjectdefs_impl.h \ + /usr/local/include/qt5/QtCore/qcoreevent.h \ + /usr/local/include/qt5/QtCore/qscopedpointer.h \ + /usr/local/include/qt5/QtCore/qmetatype.h \ + /usr/local/include/qt5/QtCore/qvarlengtharray.h \ + /usr/local/include/qt5/QtCore/qcontainerfwd.h \ + /usr/local/include/qt5/QtCore/qobject_impl.h \ + /usr/local/include/qt5/QtCore/qlocale.h \ + /usr/local/include/qt5/QtCore/qvariant.h \ + /usr/local/include/qt5/QtCore/qshareddata.h \ + /usr/local/include/qt5/QtCore/qvector.h \ + /usr/local/include/qt5/QtCore/qpoint.h \ + /usr/local/include/qt5/QtCore/qset.h \ + /usr/local/include/qt5/QtCore/qcontiguouscache.h \ + /usr/local/include/qt5/QtCore/qsharedpointer.h \ + /usr/local/include/qt5/QtCore/qsharedpointer_impl.h \ + /usr/local/include/qt5/QtCore/QTextStream \ + /usr/local/include/qt5/QtCore/QStringList \ + /usr/local/include/qt5/QtCore/QList \ + /usr/local/include/qt5/QtCore/QHash $(CXX) -c $(CXXFLAGS) $(INCPATH) -o CommandLineParser.o CommandLineParser.cpp ####### Install -install: FORCE +install_target: first FORCE + @test -d $(INSTALL_ROOT)/usr/local/bin || mkdir -p $(INSTALL_ROOT)/usr/local/bin + -$(QINSTALL_PROGRAM) ../$(QMAKE_TARGET) $(INSTALL_ROOT)/usr/local/bin/$(QMAKE_TARGET) + -$(STRIP) $(INSTALL_ROOT)/usr/local/bin/$(QMAKE_TARGET) -uninstall: FORCE +uninstall_target: FORCE + -$(DEL_FILE) $(INSTALL_ROOT)/usr/local/bin/$(QMAKE_TARGET) + -$(DEL_DIR) $(INSTALL_ROOT)/usr/local/bin/ + + +install: install_target FORCE + +uninstall: uninstall_target FORCE FORCE: Index: user/uqs/git_conv/svn2git/src/main.cpp =================================================================== --- user/uqs/git_conv/svn2git/src/main.cpp (revision 352592) +++ user/uqs/git_conv/svn2git/src/main.cpp (revision 352593) @@ -1,288 +1,292 @@ /* * Copyright (C) 2007 Thiago Macieira * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include "CommandLineParser.h" #include "ruleparser.h" #include "repository.h" #include "svn.h" QHash loadIdentityMapFile(const QString &fileName) { QHash result; if (fileName.isEmpty()) return result; QFile file(fileName); if (!file.open(QIODevice::ReadOnly)) { fprintf(stderr, "Could not open file %s: %s", qPrintable(fileName), qPrintable(file.errorString())); return result; } while (!file.atEnd()) { QByteArray line = file.readLine(); int comment_pos = line.indexOf('#'); if (comment_pos != -1) line.truncate(comment_pos); line = line.trimmed(); int space = line.indexOf(' '); if (space == -1) continue; // invalid line // Support git-svn author files, too // - svn2git native: loginname Joe User // - git-svn: loginname = Joe User int rightspace = line.indexOf(" = "); int leftspace = space; if (rightspace == -1) { rightspace = space; } else { leftspace = rightspace; rightspace += 2; } QByteArray realname = line.mid(rightspace).trimmed(); line.truncate(leftspace); result.insert(line, realname); }; file.close(); return result; } QSet loadRevisionsFile( const QString &fileName, Svn &svn ) { QRegExp revint("(\\d+)\\s*(?:-\\s*(\\d+|HEAD))?"); QSet revisions; if(fileName.isEmpty()) return revisions; QFile file(fileName); if( !file.open(QIODevice::ReadOnly)) { fprintf(stderr, "Could not open file %s: %s\n", qPrintable(fileName), qPrintable(file.errorString())); return revisions; } bool ok; while(!file.atEnd()) { QByteArray line = file.readLine().trimmed(); revint.indexIn(line); if( revint.cap(2).isEmpty() ) { int rev = revint.cap(1).toInt(&ok); if(ok) { revisions.insert(rev); } else { fprintf(stderr, "Unable to convert %s to int, skipping revision.\n", qPrintable(QString(line))); } } else if( revint.captureCount() == 2 ) { int rev = revint.cap(1).toInt(&ok); if(!ok) { fprintf(stderr, "Unable to convert %s (%s) to int, skipping revisions.\n", qPrintable(revint.cap(1)), qPrintable(QString(line))); continue; } int lastrev = 0; if(revint.cap(2) == "HEAD") { lastrev = svn.youngestRevision(); ok = true; } else { lastrev = revint.cap(2).toInt(&ok); } if(!ok) { fprintf(stderr, "Unable to convert %s (%s) to int, skipping revisions.\n", qPrintable(revint.cap(2)), qPrintable(QString(line))); continue; } for(; rev <= lastrev; ++rev ) revisions.insert(rev); } else { fprintf(stderr, "Unable to convert %s to int, skipping revision.\n", qPrintable(QString(line))); } } file.close(); return revisions; } static const CommandLineOption options[] = { {"--identity-map FILENAME", "provide map between svn username and email"}, {"--identity-domain DOMAIN", "provide user domain if no map was given"}, {"--revisions-file FILENAME", "provide a file with revision number that should be processed"}, {"--rules FILENAME[,FILENAME]", "the rules file(s) that determines what goes where"}, {"--msg-filter FILENAME", "External program / script to modify svn log message"}, {"--add-metadata", "if passed, each git commit will have svn commit info"}, {"--add-metadata-notes", "if passed, each git commit will have notes with svn commit info"}, {"--resume-from revision", "start importing at svn revision number"}, {"--max-rev revision", "stop importing at svn revision number"}, {"--dry-run", "don't actually write anything"}, {"--create-dump", "don't create the repository but a dump file suitable for piping into fast-import"}, {"--debug-rules", "print what rule is being used for each file"}, {"--commit-interval NUMBER", "if passed the cache will be flushed to git every NUMBER of commits"}, {"--stats", "after a run print some statistics about the rules"}, {"--svn-branches", "Use the contents of SVN when creating branches, Note: SVN tags are branches as well"}, {"--empty-dirs", "Add .gitignore-file for empty dirs"}, {"--svn-ignore", "Import svn-ignore-properties via .gitignore"}, {"--propcheck", "Check for svn-properties except svn-ignore"}, + {"--fast-import-timeout SECONDS", "number of seconds to wait before terminating fast-import, 0 to wait forever"}, {"-h, --help", "show help"}, {"-v, --version", "show version"}, CommandLineLastOption }; int main(int argc, char **argv) { printf("Invoked as:'"); for(int i = 0; i < argc; ++i) printf(" %s", argv[i]); printf("'\n"); CommandLineParser::init(argc, argv); CommandLineParser::addOptionDefinitions(options); Stats::init(); CommandLineParser *args = CommandLineParser::instance(); if(args->contains(QLatin1String("version"))) { printf("Git version: %s\n", VER); return 0; } if (args->contains(QLatin1String("help")) || args->arguments().count() != 1) { args->usage(QString(), "[Path to subversion repo]"); return 0; } if (args->undefinedOptions().count()) { QTextStream out(stderr); out << "svn-all-fast-export failed: "; bool first = true; foreach (QString option, args->undefinedOptions()) { if (!first) out << " : "; out << "unrecognized option or missing argument for; `" << option << "'" << endl; first = false; } return 10; } if (!args->contains("rules")) { QTextStream out(stderr); out << "svn-all-fast-export failed: please specify the rules using the 'rules' argument\n"; return 11; } if (!args->contains("identity-map") && !args->contains("identity-domain")) { QTextStream out(stderr); out << "WARNING; no identity-map or -domain specified, all commits will use default @localhost email address\n\n"; } QCoreApplication app(argc, argv); // Load the configuration RulesList rulesList(args->optionArgument(QLatin1String("rules"))); rulesList.load(); int resume_from = args->optionArgument(QLatin1String("resume-from")).toInt(); int max_rev = args->optionArgument(QLatin1String("max-rev")).toInt(); // create the repository list QHash repositories; int cutoff = resume_from ? resume_from : INT_MAX; retry: int min_rev = 1; foreach (Rules::Repository rule, rulesList.allRepositories()) { Repository *repo = createRepository(rule, repositories); if (!repo) return EXIT_FAILURE; repositories.insert(rule.name, repo); int repo_next = repo->setupIncremental(cutoff); + repo->restoreAnnotatedTags(); + repo->restoreBranchNotes(); /* * cutoff < resume_from => error exit eventually * repo_next == cutoff => probably truncated log */ if (cutoff < resume_from && repo_next == cutoff) /* * Restore the log file so we fail the next time * svn2git is invoked with the same arguments */ repo->restoreLog(); if (cutoff < min_rev) /* * We've rewound before the last revision of some * repository that we've already seen. Start over * from the beginning. (since cutoff is decreasing, * we're sure we'll make forward progress eventually) */ goto retry; if (min_rev < repo_next) min_rev = repo_next; } if (cutoff < resume_from) { qCritical() << "Cannot resume from" << resume_from << "as there are errors in revision" << cutoff; return EXIT_FAILURE; } if (min_rev < resume_from) qDebug() << "skipping revisions" << min_rev << "to" << resume_from - 1 << "as requested"; if (resume_from) min_rev = resume_from; Svn::initialize(); Svn svn(args->arguments().first()); svn.setMatchRules(rulesList.allMatchRules()); svn.setRepositories(repositories); svn.setIdentityMap(loadIdentityMapFile(args->optionArgument("identity-map"))); // Massage user input a little, no guarantees that input makes sense. QString domain = args->optionArgument("identity-domain").simplified().remove(QChar('@')); if (domain.isEmpty()) domain = QString("localhost"); svn.setIdentityDomain(domain); if (max_rev < 1) max_rev = svn.youngestRevision(); bool errors = false; QSet revisions = loadRevisionsFile(args->optionArgument(QLatin1String("revisions-file")), svn); const bool filerRevisions = !revisions.isEmpty(); for (int i = min_rev; i <= max_rev; ++i) { if(filerRevisions) { if( !revisions.contains(i) ) { printf("."); continue; } else { printf("\n"); } } if (!svn.exportRevision(i)) { errors = true; break; } } foreach (Repository *repo, repositories) { repo->finalizeTags(); + repo->saveBranchNotes(); delete repo; } Stats::instance()->printStats(); return errors ? EXIT_FAILURE : EXIT_SUCCESS; } Index: user/uqs/git_conv/svn2git/src/repository.cpp =================================================================== --- user/uqs/git_conv/svn2git/src/repository.cpp (revision 352592) +++ user/uqs/git_conv/svn2git/src/repository.cpp (revision 352593) @@ -1,1051 +1,1207 @@ /* * Copyright (C) 2007 Thiago Macieira * Copyright (C) 2009 Thomas Zander * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "repository.h" #include "CommandLineParser.h" #include +#include #include #include #include #include static const int maxSimultaneousProcesses = 100; typedef unsigned long long mark_t; static const mark_t maxMark = ULONG_MAX; class FastImportRepository : public Repository { public: + struct AnnotatedTag + { + QString supportingRef; + QByteArray svnprefix; + QByteArray author; + QByteArray log; + uint dt; + int revnum; + }; class Transaction : public Repository::Transaction { Q_DISABLE_COPY(Transaction) friend class FastImportRepository; FastImportRepository *repository; QByteArray branch; QByteArray svnprefix; QByteArray author; QByteArray log; uint datetime; int revnum; QVector merges; QStringList deletedFiles; QByteArray modifiedFiles; inline Transaction() {} public: ~Transaction(); - void commit(); + int commit(); void setAuthor(const QByteArray &author); void setDateTime(uint dt); void setLog(const QByteArray &log); void noteCopyFromBranch (const QString &prevbranch, int revFrom); void deleteFile(const QString &path); QIODevice *addFile(const QString &path, int mode, qint64 length); - void commitNote(const QByteArray ¬eText, bool append, + bool commitNote(const QByteArray ¬eText, bool append, const QByteArray &commit = QByteArray()); }; FastImportRepository(const Rules::Repository &rule); int setupIncremental(int &cutoff); + void restoreAnnotatedTags(); + void restoreBranchNotes(); void restoreLog(); ~FastImportRepository(); void reloadBranches(); int createBranch(const QString &branch, int revnum, const QString &branchFrom, int revFrom); int deleteBranch(const QString &branch, int revnum); Repository::Transaction *newTransaction(const QString &branch, const QString &svnprefix, int revnum); void createAnnotatedTag(const QString &name, const QString &svnprefix, int revnum, const QByteArray &author, uint dt, const QByteArray &log); void finalizeTags(); + void saveBranchNotes(); void commit(); bool branchExists(const QString& branch) const; const QByteArray branchNote(const QString& branch) const; void setBranchNote(const QString& branch, const QByteArray& noteText); bool hasPrefix() const; QString getName() const; Repository *getEffectiveRepository(); private: struct Branch { int created; QVector commits; QVector marks; - QByteArray note; }; - struct AnnotatedTag - { - QString supportingRef; - QByteArray svnprefix; - QByteArray author; - QByteArray log; - uint dt; - int revnum; - }; QHash branches; + QHash branchNotes; QHash annotatedTags; QString name; QString prefix; LoggingQProcess fastImport; int commitCount; int outstandingTransactions; QByteArray deletedBranches; QByteArray resetBranches; + QSet deletedBranchNames; + QSet resetBranchNames; /* Optional filter to fix up log messages */ QProcess filterMsg; QByteArray msgFilter(QByteArray); /* starts at 0, and counts up. */ mark_t last_commit_mark; - /* starts at maxMark and counts down. Reset after each SVN revision */ + /* starts at maxMark - 1 and counts down. Reset after each SVN revision */ mark_t next_file_mark; bool processHasStarted; void startFastImport(); void closeFastImport(); // called when a transaction is deleted void forgetTransaction(Transaction *t); int resetBranch(const QString &branch, int revnum, mark_t mark, const QByteArray &resetTo, const QByteArray &comment); long long markFrom(const QString &branchFrom, int branchRevNum, QByteArray &desc); friend class ProcessCache; Q_DISABLE_COPY(FastImportRepository) }; class ForwardingRepository : public Repository { QString name; Repository *repo; QString prefix; public: class Transaction : public Repository::Transaction { Q_DISABLE_COPY(Transaction) Repository::Transaction *txn; QString prefix; public: Transaction(Repository::Transaction *t, const QString &p) : txn(t), prefix(p) {} ~Transaction() { delete txn; } - void commit() { txn->commit(); } + int commit() { return txn->commit(); } void setAuthor(const QByteArray &author) { txn->setAuthor(author); } void setDateTime(uint dt) { txn->setDateTime(dt); } void setLog(const QByteArray &log) { txn->setLog(log); } void noteCopyFromBranch (const QString &prevbranch, int revFrom) { txn->noteCopyFromBranch(prevbranch, revFrom); } void deleteFile(const QString &path) { txn->deleteFile(prefix + path); } QIODevice *addFile(const QString &path, int mode, qint64 length) { return txn->addFile(prefix + path, mode, length); } - void commitNote(const QByteArray ¬eText, bool append, + bool commitNote(const QByteArray ¬eText, bool append, const QByteArray &commit) { return txn->commitNote(noteText, append, commit); } }; ForwardingRepository(const QString &n, Repository *r, const QString &p) : name(n), repo(r), prefix(p) {} int setupIncremental(int &) { return 1; } + void restoreAnnotatedTags() {} + void restoreBranchNotes() {} void restoreLog() {} void reloadBranches() { return repo->reloadBranches(); } int createBranch(const QString &branch, int revnum, const QString &branchFrom, int revFrom) { return repo->createBranch(branch, revnum, branchFrom, revFrom); } int deleteBranch(const QString &branch, int revnum) { return repo->deleteBranch(branch, revnum); } Repository::Transaction *newTransaction(const QString &branch, const QString &svnprefix, int revnum) { Repository::Transaction *t = repo->newTransaction(branch, svnprefix, revnum); return new Transaction(t, prefix); } void createAnnotatedTag(const QString &name, const QString &svnprefix, int revnum, const QByteArray &author, uint dt, const QByteArray &log) { repo->createAnnotatedTag(name, svnprefix, revnum, author, dt, log); } void finalizeTags() { /* loop that called this will invoke it on 'repo' too */ } + void saveBranchNotes() { /* loop that called this will invoke it on 'repo' too */ } void commit() { repo->commit(); } bool branchExists(const QString& branch) const { return repo->branchExists(branch); } const QByteArray branchNote(const QString& branch) const { return repo->branchNote(branch); } void setBranchNote(const QString& branch, const QByteArray& noteText) { repo->setBranchNote(branch, noteText); } bool hasPrefix() const { return !prefix.isEmpty() || repo->hasPrefix(); } QString getName() const { return name; } Repository *getEffectiveRepository() { return repo->getEffectiveRepository(); } }; class ProcessCache: QLinkedList { public: void touch(FastImportRepository *repo) { remove(repo); // if the cache is too big, remove from the front while (size() >= maxSimultaneousProcesses) takeFirst()->closeFastImport(); // append to the end append(repo); } inline void remove(FastImportRepository *repo) { #if QT_VERSION >= 0x040400 removeOne(repo); #else removeAll(repo); #endif } }; static ProcessCache processCache; +QDataStream &operator<<(QDataStream &out, const FastImportRepository::AnnotatedTag &annotatedTag) +{ + out << annotatedTag.supportingRef + << annotatedTag.svnprefix + << annotatedTag.author + << annotatedTag.log + << (quint64) annotatedTag.dt + << (qint64) annotatedTag.revnum; + return out; +} + +QDataStream &operator>>(QDataStream &in, FastImportRepository::AnnotatedTag &annotatedTag) +{ + quint64 dt; + qint64 revnum; + + in >> annotatedTag.supportingRef + >> annotatedTag.svnprefix + >> annotatedTag.author + >> annotatedTag.log + >> dt + >> revnum; + annotatedTag.dt = (uint) dt; + annotatedTag.revnum = (int) revnum; + return in; +} + Repository *createRepository(const Rules::Repository &rule, const QHash &repositories) { if (rule.forwardTo.isEmpty()) return new FastImportRepository(rule); Repository *r = repositories[rule.forwardTo]; if (!r) { qCritical() << "no repository with name" << rule.forwardTo << "found at" << rule.info(); return r; } return new ForwardingRepository(rule.name, r, rule.prefix); } static QString marksFileName(QString name) { name.replace('/', '_'); name.prepend("marks-"); return name; } +static QString annotatedTagsFileName(QString name) +{ + name.replace('/', '_'); + name.prepend("annotatedTags-"); + return name; +} + +static QString branchNotesFileName(QString name) +{ + name.replace('/', '_'); + name.prepend("branchNotes-"); + return name; +} + FastImportRepository::FastImportRepository(const Rules::Repository &rule) : name(rule.name), prefix(rule.forwardTo), fastImport(name), commitCount(0), outstandingTransactions(0), - last_commit_mark(0), next_file_mark(maxMark), processHasStarted(false) + last_commit_mark(0), next_file_mark(maxMark - 1), processHasStarted(false) { foreach (Rules::Repository::Branch branchRule, rule.branches) { Branch branch; branch.created = 1; branches.insert(branchRule.name, branch); } // create the default branch branches["master"].created = 1; - fastImport.setWorkingDirectory(name); if (!CommandLineParser::instance()->contains("dry-run") && !CommandLineParser::instance()->contains("create-dump")) { + fastImport.setWorkingDirectory(name); if (!QDir(name).exists()) { // repo doesn't exist yet. qDebug() << "Creating new repository" << name; QDir::current().mkpath(name); QProcess init; init.setWorkingDirectory(name); init.start("git", QStringList() << "--bare" << "init"); init.waitForFinished(-1); + QProcess casesensitive; + casesensitive.setWorkingDirectory(name); + casesensitive.start("git", QStringList() << "config" << "core.ignorecase" << "false"); + casesensitive.waitForFinished(-1); // Write description if (!rule.description.isEmpty()) { QFile fDesc(QDir(name).filePath("description")); if (fDesc.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) { - fDesc.write(rule.description.toUtf8()); + fDesc.write(rule.description.toUtf8()); fDesc.putChar('\n'); fDesc.close(); } } { QFile marks(name + "/" + marksFileName(name)); marks.open(QIODevice::WriteOnly); marks.close(); } } } } static QString logFileName(QString name) { name.replace('/', '_'); if (CommandLineParser::instance()->contains("create-dump")) name.append(".fi"); else name.prepend("log-"); return name; } static mark_t lastValidMark(QString name) { QFile marksfile(name + "/" + marksFileName(name)); if (!marksfile.open(QIODevice::ReadOnly)) return 0; qDebug() << "marksfile " << marksfile.fileName() ; mark_t prev_mark = 0; int lineno = 0; while (!marksfile.atEnd()) { QString line = marksfile.readLine(); ++lineno; if (line.isEmpty()) continue; mark_t mark = 0; if (line[0] == ':') { int sp = line.indexOf(' '); if (sp != -1) { QString m = line.mid(1, sp-1); mark = m.toULongLong(); } } if (!mark) { qCritical() << marksfile.fileName() << "line" << lineno << "marks file corrupt?" << "mark " << mark; return 0; } if (mark == prev_mark) { qCritical() << marksfile.fileName() << "line" << lineno << "marks file has duplicates"; return 0; } if (mark < prev_mark) { qCritical() << marksfile.fileName() << "line" << lineno << "marks file not sorted"; return 0; } if (mark > prev_mark + 1) break; prev_mark = mark; } return prev_mark; } int FastImportRepository::setupIncremental(int &cutoff) { QFile logfile(logFileName(name)); if (!logfile.exists()) return 1; logfile.open(QIODevice::ReadWrite); QRegExp progress("progress SVN r(\\d+) branch (.*) = :(\\d+)"); mark_t last_valid_mark = lastValidMark(name); int last_revnum = 0; qint64 pos = 0; int retval = 0; QString bkup = logfile.fileName() + ".old"; while (!logfile.atEnd()) { pos = logfile.pos(); QByteArray line = logfile.readLine(); int hash = line.indexOf('#'); if (hash != -1) line.truncate(hash); line = line.trimmed(); if (line.isEmpty()) continue; if (!progress.exactMatch(line)) continue; int revnum = progress.cap(1).toInt(); QString branch = progress.cap(2); mark_t mark = progress.cap(3).toULongLong(); if (revnum >= cutoff) goto beyond_cutoff; if (revnum < last_revnum) qWarning() << "WARN:" << name << "revision numbers are not monotonic: " << "got" << QString::number(last_revnum) << "and then" << QString::number(revnum); if (mark > last_valid_mark) { qWarning() << "WARN:" << name << "unknown commit mark found: rewinding -- did you hit Ctrl-C?"; cutoff = revnum; goto beyond_cutoff; } last_revnum = revnum; if (last_commit_mark < mark) last_commit_mark = mark; Branch &br = branches[branch]; if (!br.created || !mark || br.marks.isEmpty() || !br.marks.last()) br.created = revnum; br.commits.append(revnum); br.marks.append(mark); } retval = last_revnum + 1; if (retval == cutoff) /* * If a stale backup file exists already, remove it, so that * we don't confuse ourselves in 'restoreLog()' */ QFile::remove(bkup); return retval; beyond_cutoff: // backup file, since we'll truncate QFile::remove(bkup); logfile.copy(bkup); // truncate, so that we ignore the rest of the revisions qDebug() << name << "truncating history to revision" << cutoff; logfile.resize(pos); return cutoff; } +void FastImportRepository::restoreAnnotatedTags() +{ + QFile annotatedTagsFile(name + "/" + annotatedTagsFileName(name)); + if (!annotatedTagsFile.exists()) + return; + annotatedTagsFile.open(QIODevice::ReadOnly); + QDataStream annotatedTagsStream(&annotatedTagsFile); + annotatedTagsStream >> annotatedTags; + annotatedTagsFile.close(); +} + +void FastImportRepository::restoreBranchNotes() +{ + QFile branchNotesFile(name + "/" + branchNotesFileName(name)); + if (!branchNotesFile.exists()) + return; + branchNotesFile.open(QIODevice::ReadOnly); + QDataStream branchNotesStream(&branchNotesFile); + branchNotesStream >> branchNotes; + branchNotesFile.close(); +} + void FastImportRepository::restoreLog() { QString file = logFileName(name); QString bkup = file + ".old"; if (!QFile::exists(bkup)) return; QFile::remove(file); QFile::rename(bkup, file); } FastImportRepository::~FastImportRepository() { Q_ASSERT(outstandingTransactions == 0); closeFastImport(); } void FastImportRepository::closeFastImport() { if (fastImport.state() != QProcess::NotRunning) { + int fastImportTimeout = CommandLineParser::instance()->optionArgument(QLatin1String("fast-import-timeout"), QLatin1String("30")).toInt(); + if(fastImportTimeout == 0) { + qDebug() << "Waiting forever for fast-import to finish."; + fastImportTimeout = -1; + } else { + qDebug() << "Waiting" << fastImportTimeout << "seconds for fast-import to finish."; + fastImportTimeout *= 10000; + } fastImport.write("checkpoint\n"); fastImport.waitForBytesWritten(-1); fastImport.closeWriteChannel(); - if (!fastImport.waitForFinished()) { + if (!fastImport.waitForFinished(fastImportTimeout)) { fastImport.terminate(); if (!fastImport.waitForFinished(200)) qWarning() << "WARN: git-fast-import for repository" << name << "did not die"; } } processHasStarted = false; processCache.remove(this); } void FastImportRepository::reloadBranches() { bool reset_notes = false; foreach (QString branch, branches.keys()) { Branch &br = branches[branch]; if (br.marks.isEmpty() || !br.marks.last()) continue; - reset_notes = true; + reset_notes = true; QByteArray branchRef = branch.toUtf8(); if (!branchRef.startsWith("refs/")) branchRef.prepend("refs/heads/"); + startFastImport(); fastImport.write("reset " + branchRef + "\nfrom :" + QByteArray::number(br.marks.last()) + "\n\n" "progress Branch " + branchRef + " reloaded\n"); } if (reset_notes && - CommandLineParser::instance()->contains("add-metadata-notes")) { - fastImport.write("reset refs/notes/commits\nfrom :" + - QByteArray::number(maxMark + 1) + - "\n"); + CommandLineParser::instance()->contains("add-metadata-notes")) { + + startFastImport(); + fastImport.write("reset refs/notes/commits\nfrom :" + + QByteArray::number(maxMark) + + "\n"); } } long long FastImportRepository::markFrom(const QString &branchFrom, int branchRevNum, QByteArray &branchFromDesc) { Branch &brFrom = branches[branchFrom]; if (!brFrom.created) return -1; if (brFrom.commits.isEmpty()) { return -1; } if (branchRevNum == brFrom.commits.last()) { return brFrom.marks.last(); } QVector::const_iterator it = qUpperBound(brFrom.commits, branchRevNum); if (it == brFrom.commits.begin()) { return 0; } int closestCommit = *--it; if (!branchFromDesc.isEmpty()) { branchFromDesc += " at r" + QByteArray::number(branchRevNum); if (closestCommit != branchRevNum) { branchFromDesc += " => r" + QByteArray::number(closestCommit); } } return brFrom.marks[it - brFrom.commits.begin()]; } int FastImportRepository::createBranch(const QString &branch, int revnum, const QString &branchFrom, int branchRevNum) { QByteArray branchFromDesc = "from branch " + branchFrom.toUtf8(); long long mark = markFrom(branchFrom, branchRevNum, branchFromDesc); if (mark == -1) { qCritical() << branch << "in repository" << name << "is branching from branch" << branchFrom << "but the latter doesn't exist. Can't continue."; return EXIT_FAILURE; } QByteArray branchFromRef = ":" + QByteArray::number(mark); if (!mark) { qWarning() << "WARN:" << branch << "in repository" << name << "is branching but no exported commits exist in repository" << "creating an empty branch."; branchFromRef = branchFrom.toUtf8(); if (!branchFromRef.startsWith("refs/")) branchFromRef.prepend("refs/heads/"); branchFromDesc += ", deleted/unknown"; } qDebug() << "Creating branch:" << branch << "from" << branchFrom << "(" << branchRevNum << branchFromDesc << ")"; // Preserve note - branches[branch].note = branches.value(branchFrom).note; + branchNotes[branch] = branchNotes.value(branchFrom); return resetBranch(branch, revnum, mark, branchFromRef, branchFromDesc); } int FastImportRepository::deleteBranch(const QString &branch, int revnum) { static QByteArray null_sha(40, '0'); return resetBranch(branch, revnum, 0, null_sha, "delete"); } int FastImportRepository::resetBranch(const QString &branch, int revnum, mark_t mark, const QByteArray &resetTo, const QByteArray &comment) { QByteArray branchRef = branch.toUtf8(); if (!branchRef.startsWith("refs/")) branchRef.prepend("refs/heads/"); Branch &br = branches[branch]; QByteArray backupCmd; if (br.created && br.created != revnum && !br.marks.isEmpty() && br.marks.last()) { QByteArray backupBranch; if ((comment == "delete") && branchRef.startsWith("refs/heads/")) backupBranch = "refs/tags/backups/" + branchRef.mid(11) + "@" + QByteArray::number(revnum); else backupBranch = "refs/backups/r" + QByteArray::number(revnum) + branchRef.mid(4); qWarning() << "WARN: backing up branch" << branch << "to" << backupBranch; backupCmd = "reset " + backupBranch + "\nfrom " + branchRef + "\n\n"; } br.created = revnum; br.commits.append(revnum); br.marks.append(mark); QByteArray cmd = "reset " + branchRef + "\nfrom " + resetTo + "\n\n" "progress SVN r" + QByteArray::number(revnum) + " branch " + branch.toUtf8() + " = :" + QByteArray::number(mark) + " # " + comment + "\n\n"; - if(comment == "delete") + if(comment == "delete") { deletedBranches.append(backupCmd).append(cmd); - else + deletedBranchNames.insert(branchRef); + } else { resetBranches.append(backupCmd).append(cmd); + resetBranchNames.insert(branchRef); + } return EXIT_SUCCESS; } void FastImportRepository::commit() { if (deletedBranches.isEmpty() && resetBranches.isEmpty()) { return; } startFastImport(); fastImport.write(deletedBranches); fastImport.write(resetBranches); deletedBranches.clear(); resetBranches.clear(); + QSet::ConstIterator it = deletedBranchNames.constBegin(); + for ( ; it != deletedBranchNames.constEnd(); ++it) { + QString tagName = *it; + if (resetBranchNames.contains(tagName)) + continue; + if (tagName.startsWith("refs/tags/")) + tagName.remove(0, 10); + if (annotatedTags.remove(tagName) > 0) { + qDebug() << "Removing annotated tag" << tagName << "for" << name; + } + } + deletedBranchNames.clear(); + resetBranchNames.clear(); } Repository::Transaction *FastImportRepository::newTransaction(const QString &branch, const QString &svnprefix, int revnum) { if (!branches.contains(branch)) { qWarning() << "WARN: Transaction:" << branch << "is not a known branch in repository" << name << endl << "Going to create it automatically"; } Transaction *txn = new Transaction; txn->repository = this; txn->branch = branch.toUtf8(); txn->svnprefix = svnprefix.toUtf8(); txn->datetime = 0; txn->revnum = revnum; if ((++commitCount % CommandLineParser::instance()->optionArgument(QLatin1String("commit-interval"), QLatin1String("10000")).toInt()) == 0) { startFastImport(); // write everything to disk every 10000 commits fastImport.write("checkpoint\n"); - qDebug() << "checkpoint!, marks file trunkated"; + qDebug() << "checkpoint!, marks file truncated"; } outstandingTransactions++; return txn; } void FastImportRepository::forgetTransaction(Transaction *) { if (!--outstandingTransactions) - next_file_mark = maxMark; + next_file_mark = maxMark - 1; } void FastImportRepository::createAnnotatedTag(const QString &ref, const QString &svnprefix, int revnum, const QByteArray &author, uint dt, const QByteArray &log) { QString tagName = ref; if (tagName.startsWith("refs/tags/")) tagName.remove(0, 10); if (!annotatedTags.contains(tagName)) - printf("Creating annotated tag %s (%s)\n", qPrintable(tagName), qPrintable(ref)); + printf("\nCreating annotated tag %s (%s) for %s\n", qPrintable(tagName), qPrintable(ref), qPrintable(name)); else - printf("Re-creating annotated tag %s\n", qPrintable(tagName)); + printf("\nRe-creating annotated tag %s for %s\n", qPrintable(tagName), qPrintable(name)); AnnotatedTag &tag = annotatedTags[tagName]; tag.supportingRef = ref; tag.svnprefix = svnprefix.toUtf8(); tag.revnum = revnum; tag.author = author; tag.log = log; tag.dt = dt; } void FastImportRepository::finalizeTags() { if (annotatedTags.isEmpty()) return; - printf("Finalising tags for %s...", qPrintable(name)); + QFile annotatedTagsFile(name + "/" + annotatedTagsFileName(name)); + annotatedTagsFile.open(QIODevice::WriteOnly); + QDataStream annotatedTagsStream(&annotatedTagsFile); + annotatedTagsStream << annotatedTags; + annotatedTagsFile.close(); + + printf("Finalising annotated tags for %s...", qPrintable(name)); startFastImport(); QHash::ConstIterator it = annotatedTags.constBegin(); for ( ; it != annotatedTags.constEnd(); ++it) { const QString &tagName = it.key(); const AnnotatedTag &tag = it.value(); QByteArray message = tag.log; if (!message.endsWith('\n')) message += '\n'; if (CommandLineParser::instance()->contains("add-metadata")) message += "\n" + formatMetadataMessage(tag.svnprefix, tag.revnum, tagName.toUtf8()); { QByteArray branchRef = tag.supportingRef.toUtf8(); if (!branchRef.startsWith("refs/")) branchRef.prepend("refs/heads/"); QByteArray s = "progress Creating annotated tag " + tagName.toUtf8() + " from ref " + branchRef + "\n" + "tag " + tagName.toUtf8() + "\n" + "from " + branchRef + "\n" + "tagger " + tag.author + ' ' + QByteArray::number(tag.dt) + " +0000" + "\n" + "data " + QByteArray::number( message.length() ) + "\n"; fastImport.write(s); } fastImport.write(message); fastImport.putChar('\n'); if (!fastImport.waitForBytesWritten(-1)) qFatal("Failed to write to process: %s", qPrintable(fastImport.errorString())); // Append note to the tip commit of the supporting ref. There is no // easy way to attach a note to the tag itself with fast-import. if (CommandLineParser::instance()->contains("add-metadata-notes")) { Repository::Transaction *txn = newTransaction(tag.supportingRef, tag.svnprefix, tag.revnum); txn->setAuthor(tag.author); txn->setDateTime(tag.dt); - txn->commitNote(formatMetadataMessage(tag.svnprefix, tag.revnum, tagName.toUtf8()), true); + bool written = txn->commitNote(formatMetadataMessage(tag.svnprefix, tag.revnum, tagName.toUtf8()), true); delete txn; - if (!fastImport.waitForBytesWritten(-1)) + if (written && !fastImport.waitForBytesWritten(-1)) qFatal("Failed to write to process: %s", qPrintable(fastImport.errorString())); } printf(" %s", qPrintable(tagName)); fflush(stdout); } while (fastImport.bytesToWrite()) if (!fastImport.waitForBytesWritten(-1)) qFatal("Failed to write to process: %s", qPrintable(fastImport.errorString())); printf("\n"); } +void FastImportRepository::saveBranchNotes() +{ + if (branchNotes.isEmpty()) + return; + QFile branchNotesFile(name + "/" + branchNotesFileName(name)); + branchNotesFile.open(QIODevice::WriteOnly); + QDataStream branchNotesStream(&branchNotesFile); + branchNotesStream << branchNotes; + branchNotesFile.close(); +} + QByteArray FastImportRepository::msgFilter(QByteArray msg) { QByteArray output = msg; if (CommandLineParser::instance()->contains("msg-filter")) { - if (filterMsg.state() == QProcess::Running) - qFatal("filter process already running?"); + if (filterMsg.state() == QProcess::Running) + qFatal("filter process already running?"); - filterMsg.start(CommandLineParser::instance()->optionArgument("msg-filter")); + filterMsg.start(CommandLineParser::instance()->optionArgument("msg-filter")); - if(!(filterMsg.waitForStarted(-1))) - qFatal("Failed to Start Filter %d %s", __LINE__, qPrintable(filterMsg.errorString())); + if(!(filterMsg.waitForStarted(-1))) + qFatal("Failed to Start Filter %d %s", __LINE__, qPrintable(filterMsg.errorString())); - filterMsg.write(msg); - filterMsg.closeWriteChannel(); - filterMsg.waitForFinished(); - output = filterMsg.readAllStandardOutput(); + filterMsg.write(msg); + filterMsg.closeWriteChannel(); + filterMsg.waitForFinished(); + output = filterMsg.readAllStandardOutput(); } return output; } void FastImportRepository::startFastImport() { processCache.touch(this); if (fastImport.state() == QProcess::NotRunning) { if (processHasStarted) qFatal("git-fast-import has been started once and crashed?"); processHasStarted = true; // start the process QString marksFile = marksFileName(name); QStringList marksOptions; marksOptions << "--import-marks=" + marksFile; marksOptions << "--export-marks=" + marksFile; marksOptions << "--force"; fastImport.setStandardOutputFile(logFileName(name), QIODevice::Append); fastImport.setProcessChannelMode(QProcess::MergedChannels); if (!CommandLineParser::instance()->contains("dry-run") && !CommandLineParser::instance()->contains("create-dump")) { fastImport.start("git", QStringList() << "fast-import" << marksOptions); } else { - fastImport.start("/bin/cat", QStringList()); + fastImport.start("cat", QStringList()); } fastImport.waitForStarted(-1); reloadBranches(); } } QByteArray Repository::formatMetadataMessage(const QByteArray &svnprefix, int revnum, const QByteArray &tag) { QByteArray msg = "svn path=" + svnprefix + "; revision=" + QByteArray::number(revnum); if (!tag.isEmpty()) msg += "; tag=" + tag; msg += "\n"; return msg; } bool FastImportRepository::branchExists(const QString& branch) const { return branches.contains(branch); } const QByteArray FastImportRepository::branchNote(const QString& branch) const { - return branches.value(branch).note; + return branchNotes.value(branch); } void FastImportRepository::setBranchNote(const QString& branch, const QByteArray& noteText) { if (branches.contains(branch)) - branches[branch].note = noteText; + branchNotes[branch] = noteText; } bool FastImportRepository::hasPrefix() const { return !prefix.isEmpty(); } QString FastImportRepository::getName() const { return name; } Repository *FastImportRepository::getEffectiveRepository() { return this; } FastImportRepository::Transaction::~Transaction() { repository->forgetTransaction(this); } void FastImportRepository::Transaction::setAuthor(const QByteArray &a) { author = a; } void FastImportRepository::Transaction::setDateTime(uint dt) { datetime = dt; } void FastImportRepository::Transaction::setLog(const QByteArray &l) { log = l; } void FastImportRepository::Transaction::noteCopyFromBranch(const QString &branchFrom, int branchRevNum) { if(branch == branchFrom) { qWarning() << "WARN: Cannot merge inside a branch"; return; } static QByteArray dummy; long long mark = repository->markFrom(branchFrom, branchRevNum, dummy); Q_ASSERT(dummy.isEmpty()); if (mark == -1) { qWarning() << "WARN:" << branch << "is copying from branch" << branchFrom << "but the latter doesn't exist. Continuing, assuming the files exist."; } else if (mark == 0) { qWarning() << "WARN: Unknown revision r" << QByteArray::number(branchRevNum) << ". Continuing, assuming the files exist."; } else { qWarning() << "WARN: repository " + repository->name + " branch " + branch + " has some files copied from " + branchFrom + "@" + QByteArray::number(branchRevNum); if (!merges.contains(mark)) { merges.append(mark); qDebug() << "adding" << branchFrom + "@" + QByteArray::number(branchRevNum) << ":" << mark << "as a merge point"; } else { qDebug() << "merge point already recorded"; } } } void FastImportRepository::Transaction::deleteFile(const QString &path) { QString pathNoSlash = repository->prefix + path; if(pathNoSlash.endsWith('/')) pathNoSlash.chop(1); deletedFiles.append(pathNoSlash); } QIODevice *FastImportRepository::Transaction::addFile(const QString &path, int mode, qint64 length) { mark_t mark = repository->next_file_mark--; // in case the two mark allocations meet, we might as well just abort Q_ASSERT(mark > repository->last_commit_mark + 1); if (modifiedFiles.capacity() == 0) modifiedFiles.reserve(2048); modifiedFiles.append("M "); modifiedFiles.append(QByteArray::number(mode, 8)); modifiedFiles.append(" :"); modifiedFiles.append(QByteArray::number(mark)); modifiedFiles.append(' '); modifiedFiles.append(repository->prefix + path.toUtf8()); modifiedFiles.append("\n"); + // it is returned for being written to, so start the process in any case + repository->startFastImport(); if (!CommandLineParser::instance()->contains("dry-run")) { - repository->startFastImport(); repository->fastImport.writeNoLog("blob\nmark :"); repository->fastImport.writeNoLog(QByteArray::number(mark)); repository->fastImport.writeNoLog("\ndata "); repository->fastImport.writeNoLog(QByteArray::number(length)); repository->fastImport.writeNoLog("\n", 1); } return &repository->fastImport; } -void FastImportRepository::Transaction::commitNote(const QByteArray ¬eText, bool append, const QByteArray &commit) +bool FastImportRepository::Transaction::commitNote(const QByteArray ¬eText, bool append, const QByteArray &commit) { QByteArray branchRef = branch; if (!branchRef.startsWith("refs/")) + { branchRef.prepend("refs/heads/"); + } const QByteArray &commitRef = commit.isNull() ? branchRef : commit; QByteArray message = "Adding Git note for current " + commitRef + "\n"; QByteArray text = noteText; + if (noteText[noteText.size() - 1] != '\n') + { + text += '\n'; + } + QByteArray branchNote = repository->branchNote(branch); + if (!branchNote.isEmpty() && (branchNote[branchNote.size() - 1] != '\n')) + { + branchNote += '\n'; + } if (append && commit.isNull() && repository->branchExists(branch) && - !repository->branchNote(branch).isEmpty()) + !branchNote.isEmpty()) { - text = repository->branchNote(branch) + text; + int i = branchNote.indexOf(text); + if ((i == 0) || ((i != -1) && (branchNote[i - 1] == '\n'))) + { + // note is already present at the start or somewhere within following a newline + return false; + } + text = branchNote + text; message = "Appending Git note for current " + commitRef + "\n"; } QByteArray s(""); s.append("commit refs/notes/commits\n"); - s.append("mark :" + QByteArray::number(maxMark + 1) + "\n"); + s.append("mark :" + QByteArray::number(maxMark) + "\n"); s.append("committer " + author + " " + QString::number(datetime) + " +0000" + "\n"); s.append("data " + QString::number(message.length()) + "\n"); s.append(message + "\n"); s.append("N inline " + commitRef + "\n"); s.append("data " + QString::number(text.length()) + "\n"); s.append(text + "\n"); + repository->startFastImport(); repository->fastImport.write(s); - if (commit.isNull()) { + if (commit.isNull()) + { repository->setBranchNote(QString::fromUtf8(branch), text); } + + return true; } -void FastImportRepository::Transaction::commit() +int FastImportRepository::Transaction::commit() { + foreach (QString branchName, repository->branches.keys()) + { + if (branchName.toUtf8().startsWith(branch + "/") || branch.startsWith((branchName + "/").toUtf8())) + { + qCritical() << "Branch" << branch << "conflicts with already existing branch" << branchName; + return EXIT_FAILURE; + } + } + repository->startFastImport(); // We might be tempted to use the SVN revision number as the fast-import commit mark. - // However, a single SVN revision can modify multple branches, and thus lead to multiple + // However, a single SVN revision can modify multiple branches, and thus lead to multiple // commits in the same repo. So, we need to maintain a separate commit mark counter. mark_t mark = ++repository->last_commit_mark; // in case the two mark allocations meet, we might as well just abort Q_ASSERT(mark < repository->next_file_mark - 1); // create the commit message QByteArray message = log; if (!message.endsWith('\n')) message += '\n'; if (CommandLineParser::instance()->contains("add-metadata")) message += "\n" + Repository::formatMetadataMessage(svnprefix, revnum); // Call external message filter if provided message = repository->msgFilter(message); mark_t parentmark = 0; Branch &br = repository->branches[branch]; if (br.created && !br.marks.isEmpty() && br.marks.last()) { parentmark = br.marks.last(); } else { - qWarning() << "WARN: Branch" << branch << "in repository" << repository->name << "doesn't exist at revision" - << revnum << "-- did you resume from the wrong revision?"; + if (revnum > 1) { + // Any branch at revision 1 isn't going to exist, so lets not alarm the user. + qWarning() << "WARN: Branch" << branch << "in repository" << repository->name << "doesn't exist at revision" + << revnum << "-- did you resume from the wrong revision?"; + } br.created = revnum; } br.commits.append(revnum); br.marks.append(mark); QByteArray branchRef = branch; if (!branchRef.startsWith("refs/")) branchRef.prepend("refs/heads/"); QByteArray s(""); s.append("commit " + branchRef + "\n"); s.append("mark :" + QByteArray::number(mark) + "\n"); s.append("committer " + author + " " + QString::number(datetime).toUtf8() + " +0000" + "\n"); s.append("data " + QString::number(message.length()) + "\n"); s.append(message + "\n"); repository->fastImport.write(s); // note some of the inferred merges QByteArray desc = ""; - mark_t i = !!parentmark; // if parentmark != 0, there's at least one parent + mark_t i = !!parentmark; // if parentmark != 0, there's at least one parent if(log.contains("This commit was manufactured by cvs2svn") && merges.count() > 1) { qSort(merges); repository->fastImport.write("merge :" + QByteArray::number(merges.last()) + "\n"); merges.pop_back(); qWarning() << "WARN: Discarding all but the highest merge point as a workaround for cvs2svn created branch/tag" << "Discarded marks:" << merges; } else { foreach (const mark_t merge, merges) { if (merge == parentmark) { qDebug() << "Skipping marking" << merge << "as a merge point as it matches the parent"; continue; } if (++i > 16) { // FIXME: options: // (1) ignore the 16 parent limit // (2) don't emit more than 16 parents // (3) create another commit on branch to soak up additional parents // we've chosen option (2) for now, since only artificial commits // created by cvs2svn seem to have this issue qWarning() << "WARN: too many merge parents"; break; } QByteArray m = " :" + QByteArray::number(merge); desc += m; repository->fastImport.write("merge" + m + "\n"); } } // write the file deletions if (deletedFiles.contains("")) repository->fastImport.write("deleteall\n"); else foreach (QString df, deletedFiles) repository->fastImport.write("D " + df.toUtf8() + "\n"); // write the file modifications repository->fastImport.write(modifiedFiles); repository->fastImport.write("\nprogress SVN r" + QByteArray::number(revnum) + " branch " + branch + " = :" + QByteArray::number(mark) + (desc.isEmpty() ? "" : " # merge from") + desc + "\n\n"); printf(" %d modifications from SVN %s to %s/%s", deletedFiles.count() + modifiedFiles.count('\n'), svnprefix.data(), qPrintable(repository->name), branch.data()); // Commit metadata note if requested if (CommandLineParser::instance()->contains("add-metadata-notes")) commitNote(Repository::formatMetadataMessage(svnprefix, revnum), false); while (repository->fastImport.bytesToWrite()) if (!repository->fastImport.waitForBytesWritten(-1)) qFatal("Failed to write to process: %s for repository %s", qPrintable(repository->fastImport.errorString()), qPrintable(repository->name)); + + return EXIT_SUCCESS; } Index: user/uqs/git_conv/svn2git/src/repository.h =================================================================== --- user/uqs/git_conv/svn2git/src/repository.h (revision 352592) +++ user/uqs/git_conv/svn2git/src/repository.h (revision 352593) @@ -1,149 +1,152 @@ /* * Copyright (C) 2007 Thiago Macieira * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef REPOSITORY_H #define REPOSITORY_H #include #include #include #include #include "ruleparser.h" #include "CommandLineParser.h" class LoggingQProcess : public QProcess { QFile log; bool logging; public: LoggingQProcess(const QString filename) : QProcess(), log() { if(CommandLineParser::instance()->contains("debug-rules")) { logging = true; QString name = filename; name.replace('/', '_'); name.prepend("gitlog-"); log.setFileName(name); log.open(QIODevice::WriteOnly); } else { logging = false; } }; ~LoggingQProcess() { if(logging) { log.close(); } }; qint64 write(const char *data) { Q_ASSERT(state() == QProcess::Running); if(logging) { log.write(data); } return QProcess::write(data); } qint64 write(const char *data, qint64 length) { Q_ASSERT(state() == QProcess::Running); if(logging) { log.write(data); } return QProcess::write(data, length); } qint64 write(const QByteArray &data) { Q_ASSERT(state() == QProcess::Running); if(logging) { log.write(data); } return QProcess::write(data); } qint64 writeNoLog(const char *data) { Q_ASSERT(state() == QProcess::Running); return QProcess::write(data); } qint64 writeNoLog(const char *data, qint64 length) { Q_ASSERT(state() == QProcess::Running); return QProcess::write(data, length); } qint64 writeNoLog(const QByteArray &data) { Q_ASSERT(state() == QProcess::Running); return QProcess::write(data); } bool putChar( char c) { Q_ASSERT(state() == QProcess::Running); if(logging) { log.putChar(c); } return QProcess::putChar(c); } }; class Repository { public: class Transaction { Q_DISABLE_COPY(Transaction) protected: Transaction() {} public: virtual ~Transaction() {} - virtual void commit() = 0; + virtual int commit() = 0; virtual void setAuthor(const QByteArray &author) = 0; virtual void setDateTime(uint dt) = 0; virtual void setLog(const QByteArray &log) = 0; virtual void noteCopyFromBranch (const QString &prevbranch, int revFrom) = 0; virtual void deleteFile(const QString &path) = 0; virtual QIODevice *addFile(const QString &path, int mode, qint64 length) = 0; - virtual void commitNote(const QByteArray ¬eText, bool append, + virtual bool commitNote(const QByteArray ¬eText, bool append, const QByteArray &commit = QByteArray()) = 0; }; virtual int setupIncremental(int &cutoff) = 0; + virtual void restoreAnnotatedTags() = 0; + virtual void restoreBranchNotes() = 0; virtual void restoreLog() = 0; virtual ~Repository() {} virtual void reloadBranches() = 0; virtual int createBranch(const QString &branch, int revnum, const QString &branchFrom, int revFrom) = 0; virtual int deleteBranch(const QString &branch, int revnum) = 0; virtual Repository::Transaction *newTransaction(const QString &branch, const QString &svnprefix, int revnum) = 0; virtual void createAnnotatedTag(const QString &name, const QString &svnprefix, int revnum, const QByteArray &author, uint dt, const QByteArray &log) = 0; virtual void finalizeTags() = 0; + virtual void saveBranchNotes() = 0; virtual void commit() = 0; static QByteArray formatMetadataMessage(const QByteArray &svnprefix, int revnum, const QByteArray &tag = QByteArray()); virtual bool branchExists(const QString& branch) const = 0; virtual const QByteArray branchNote(const QString& branch) const = 0; virtual void setBranchNote(const QString& branch, const QByteArray& noteText) = 0; virtual bool hasPrefix() const = 0; virtual QString getName() const = 0; virtual Repository *getEffectiveRepository() = 0; }; Repository *createRepository(const Rules::Repository &rule, const QHash &repositories); #endif Index: user/uqs/git_conv/svn2git/src/src.pro =================================================================== --- user/uqs/git_conv/svn2git/src/src.pro (revision 352592) +++ user/uqs/git_conv/svn2git/src/src.pro (revision 352593) @@ -1,36 +1,44 @@ -###################################################################### -# Automatically generated by qmake (2.01a) dim. dc. 23 13:49:28 2007 -###################################################################### - -SVN_INCLUDE = /usr/include/subversion-1 /usr/local/include/subversion-1 -APR_INCLUDE = /usr/include/apr-1.0 /usr/include/apr-1 /usr/local/include/apr-1 +if(!defined(SVN_INCLUDE, var)) { + SVN_INCLUDE = /usr/include/subversion-1 /usr/local/include/subversion-1 +} +if(!defined(APR_INCLUDE, var)) { + APR_INCLUDE = /usr/include/apr-1.0 /usr/include/apr-1 /usr/local/include/apr-1 +} exists(local-config.pri):include(local-config.pri) -VERSION = $$system(git --no-pager show --pretty=oneline --no-notes | head -1 | cut -b-40) - !isEmpty(VERSION){ - VERSION = $${VERSION} - } +if(!defined(VERSION, var)) { + VERSION = $$system(git --no-pager show --pretty=oneline --no-notes | head -1 | cut -b-40) +} VERSTR = '\\"$${VERSION}\\"' # place quotes around the version string DEFINES += VER=\"$${VERSTR}\" # create a VER macro containing the version string TEMPLATE = app TARGET = ../svn-all-fast-export + +isEmpty(PREFIX) { + PREFIX = /usr/local +} +BINDIR = $$PREFIX/bin + +INSTALLS += target +target.path = $$BINDIR + DEPENDPATH += . QT = core INCLUDEPATH += . $$SVN_INCLUDE $$APR_INCLUDE -!isEmpty($$SVN_LIBDIR): LIBS += -L$$SVN_LIBDIR +!isEmpty(SVN_LIBDIR): LIBS += -L$$SVN_LIBDIR LIBS += -lsvn_fs-1 -lsvn_repos-1 -lapr-1 -lsvn_subr-1 # Input SOURCES += ruleparser.cpp \ repository.cpp \ svn.cpp \ main.cpp \ CommandLineParser.cpp \ HEADERS += ruleparser.h \ repository.h \ svn.h \ CommandLineParser.h \ Index: user/uqs/git_conv/svn2git/src/svn.cpp =================================================================== --- user/uqs/git_conv/svn2git/src/svn.cpp (revision 352592) +++ user/uqs/git_conv/svn2git/src/svn.cpp (revision 352593) @@ -1,1052 +1,1107 @@ /* * Copyright (C) 2007 Thiago Macieira * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * Based on svn-fast-export by Chris Lee * License: MIT * URL: git://repo.or.cz/fast-import.git http://repo.or.cz/w/fast-export.git */ #define _XOPEN_SOURCE #define _LARGEFILE_SUPPORT #define _LARGEFILE64_SUPPORT #include "svn.h" #include "CommandLineParser.h" #include #include #include #include #include #include #include #include #include #include #include #include +#include #include #include #include "repository.h" #undef SVN_ERR #define SVN_ERR(expr) SVN_INT_ERR(expr) +#if SVN_VER_MAJOR == 1 && SVN_VER_MINOR < 9 +#define svn_stream_read_full svn_stream_read +#endif + typedef QList MatchRuleList; typedef QHash RepositoryHash; typedef QHash IdentityHash; class AprAutoPool { apr_pool_t *pool; AprAutoPool(const AprAutoPool &); AprAutoPool &operator=(const AprAutoPool &); public: inline AprAutoPool(apr_pool_t *parent = NULL) { - pool = svn_pool_create(parent); - } - inline ~AprAutoPool() + pool = svn_pool_create(parent); + } + inline ~AprAutoPool() { - svn_pool_destroy(pool); - } + svn_pool_destroy(pool); + } inline void clear() { svn_pool_clear(pool); } inline apr_pool_t *data() const { return pool; } inline operator apr_pool_t *() const { return pool; } }; class SvnPrivate { public: QList allMatchRules; RepositoryHash repositories; IdentityHash identities; QString userdomain; SvnPrivate(const QString &pathToRepository); ~SvnPrivate(); int youngestRevision(); int exportRevision(int revnum); int openRepository(const QString &pathToRepository); private: AprAutoPool global_pool; AprAutoPool scratch_pool; svn_fs_t *fs; svn_revnum_t youngest_rev; }; void Svn::initialize() { // initialize APR or exit if (apr_initialize() != APR_SUCCESS) { fprintf(stderr, "You lose at apr_initialize().\n"); exit(1); } // static destructor static struct Destructor { ~Destructor() { apr_terminate(); } } destructor; } Svn::Svn(const QString &pathToRepository) : d(new SvnPrivate(pathToRepository)) { } Svn::~Svn() { delete d; } void Svn::setMatchRules(const QList &allMatchRules) { d->allMatchRules = allMatchRules; } void Svn::setRepositories(const RepositoryHash &repositories) { d->repositories = repositories; } void Svn::setIdentityMap(const IdentityHash &identityMap) { d->identities = identityMap; } void Svn::setIdentityDomain(const QString &identityDomain) { d->userdomain = identityDomain; } int Svn::youngestRevision() { return d->youngestRevision(); } bool Svn::exportRevision(int revnum) { return d->exportRevision(revnum) == EXIT_SUCCESS; } SvnPrivate::SvnPrivate(const QString &pathToRepository) : global_pool(NULL) , scratch_pool(NULL) { if( openRepository(pathToRepository) != EXIT_SUCCESS) { qCritical() << "Failed to open repository"; exit(1); } // get the youngest revision svn_fs_youngest_rev(&youngest_rev, fs, global_pool); } SvnPrivate::~SvnPrivate() {} int SvnPrivate::youngestRevision() { return youngest_rev; } int SvnPrivate::openRepository(const QString &pathToRepository) { svn_repos_t *repos; QString path = pathToRepository; while (path.endsWith('/')) // no trailing slash allowed path = path.mid(0, path.length()-1); +#if SVN_VER_MAJOR == 1 && SVN_VER_MINOR < 9 + SVN_ERR(svn_repos_open2(&repos, QFile::encodeName(path), NULL, global_pool)); +#else SVN_ERR(svn_repos_open3(&repos, QFile::encodeName(path), NULL, global_pool, scratch_pool)); +#endif fs = svn_repos_fs(repos); return EXIT_SUCCESS; } enum RuleType { AnyRule = 0, NoIgnoreRule = 0x01, NoRecurseRule = 0x02 }; static MatchRuleList::ConstIterator findMatchRule(const MatchRuleList &matchRules, int revnum, const QString ¤t, int ruleMask = AnyRule) { MatchRuleList::ConstIterator it = matchRules.constBegin(), end = matchRules.constEnd(); for ( ; it != end; ++it) { if (it->minRevision > revnum) continue; if (it->maxRevision != -1 && it->maxRevision < revnum) continue; if (it->action == Rules::Match::Ignore && ruleMask & NoIgnoreRule) continue; if (it->action == Rules::Match::Recurse && ruleMask & NoRecurseRule) continue; if (it->rx.indexIn(current) == 0) { Stats::instance()->ruleMatched(*it, revnum); return it; } } // no match return end; } static int pathMode(svn_fs_root_t *fs_root, const char *pathname, apr_pool_t *pool) { svn_string_t *propvalue; SVN_ERR(svn_fs_node_prop(&propvalue, fs_root, pathname, "svn:executable", pool)); int mode = 0100644; if (propvalue) mode = 0100755; return mode; } svn_error_t *QIODevice_write(void *baton, const char *data, apr_size_t *len) { QIODevice *device = reinterpret_cast(baton); device->write(data, *len); while (device->bytesToWrite() > 32*1024) { if (!device->waitForBytesWritten(-1)) { qFatal("Failed to write to process: %s", qPrintable(device->errorString())); return svn_error_createf(APR_EOF, SVN_NO_ERROR, "Failed to write to process: %s", qPrintable(device->errorString())); } } return SVN_NO_ERROR; } static svn_stream_t *streamForDevice(QIODevice *device, apr_pool_t *pool) { svn_stream_t *stream = svn_stream_create(device, pool); svn_stream_set_write(stream, QIODevice_write); return stream; } static int dumpBlob(Repository::Transaction *txn, svn_fs_root_t *fs_root, const char *pathname, const QString &finalPathName, apr_pool_t *pool) { AprAutoPool dumppool(pool); // what type is it? int mode = pathMode(fs_root, pathname, dumppool); svn_filesize_t stream_length; SVN_ERR(svn_fs_file_length(&stream_length, fs_root, pathname, dumppool)); svn_stream_t *in_stream, *out_stream; if (!CommandLineParser::instance()->contains("dry-run")) { // open the file SVN_ERR(svn_fs_file_contents(&in_stream, fs_root, pathname, dumppool)); } // maybe it's a symlink? svn_string_t *propvalue; SVN_ERR(svn_fs_node_prop(&propvalue, fs_root, pathname, "svn:special", dumppool)); if (propvalue) { apr_size_t len = strlen("link "); if (!CommandLineParser::instance()->contains("dry-run")) { QByteArray buf; buf.reserve(len); SVN_ERR(svn_stream_read_full(in_stream, buf.data(), &len)); if (len == strlen("link ") && strncmp(buf, "link ", len) == 0) { mode = 0120000; stream_length -= len; } else { //this can happen if a link changed into a file in one commit qWarning("file %s is svn:special but not a symlink", pathname); // re-open the file as we tried to read "link " svn_stream_close(in_stream); SVN_ERR(svn_fs_file_contents(&in_stream, fs_root, pathname, dumppool)); } } } QIODevice *io = txn->addFile(finalPathName, mode, stream_length); if (!CommandLineParser::instance()->contains("dry-run")) { // open a generic svn_stream_t for the QIODevice out_stream = streamForDevice(io, dumppool); SVN_ERR(svn_stream_copy3(in_stream, out_stream, NULL, NULL, dumppool)); // print an ending newline io->putChar('\n'); } return EXIT_SUCCESS; } -static int recursiveDumpDir(Repository::Transaction *txn, svn_fs_root_t *fs_root, +static bool wasDir(svn_fs_t *fs, int revnum, const char *pathname, apr_pool_t *pool) +{ + AprAutoPool subpool(pool); + svn_fs_root_t *fs_root; + if (svn_fs_revision_root(&fs_root, fs, revnum, subpool) != SVN_NO_ERROR) + return false; + + svn_boolean_t is_dir; + if (svn_fs_is_dir(&is_dir, fs_root, pathname, subpool) != SVN_NO_ERROR) + return false; + + return is_dir; +} + +static int recursiveDumpDir(Repository::Transaction *txn, svn_fs_t *fs, svn_fs_root_t *fs_root, const QByteArray &pathname, const QString &finalPathName, - apr_pool_t *pool) + apr_pool_t *pool, svn_revnum_t revnum, + const Rules::Match &rule, const MatchRuleList &matchRules, + bool ruledebug) { + if (!wasDir(fs, revnum, pathname.data(), pool)) { + if (dumpBlob(txn, fs_root, pathname, finalPathName, pool) == EXIT_FAILURE) + return EXIT_FAILURE; + return EXIT_SUCCESS; + } + // get the dir listing apr_hash_t *entries; SVN_ERR(svn_fs_dir_entries(&entries, fs_root, pathname, pool)); AprAutoPool dirpool(pool); // While we get a hash, put it in a map for sorted lookup, so we can // repeat the conversions and get the same git commit hashes. QMap map; for (apr_hash_index_t *i = apr_hash_first(pool, entries); i; i = apr_hash_next(i)) { const void *vkey; void *value; apr_hash_this(i, &vkey, NULL, &value); svn_fs_dirent_t *dirent = reinterpret_cast(value); map.insertMulti(QByteArray(dirent->name), dirent->kind); } QMapIterator i(map); while (i.hasNext()) { dirpool.clear(); i.next(); QByteArray entryName = pathname + '/' + i.key(); QString entryFinalName = finalPathName + QString::fromUtf8(i.key()); if (i.value() == svn_node_dir) { entryFinalName += '/'; - if (recursiveDumpDir(txn, fs_root, entryName, entryFinalName, dirpool) == EXIT_FAILURE) + QString entryNameQString = entryName + '/'; + + MatchRuleList::ConstIterator match = findMatchRule(matchRules, revnum, entryNameQString); + if (match == matchRules.constEnd()) continue; // no match of parent repo? (should not happen) + + const Rules::Match &matchedRule = *match; + if (matchedRule.action != Rules::Match::Export || matchedRule.repository != rule.repository) { + if (ruledebug) + qDebug() << "recursiveDumpDir:" << entryNameQString << "skip entry for different/ignored repository"; + continue; + } + + if (recursiveDumpDir(txn, fs, fs_root, entryName, entryFinalName, dirpool, revnum, rule, matchRules, ruledebug) == EXIT_FAILURE) return EXIT_FAILURE; } else if (i.value() == svn_node_file) { printf("+"); fflush(stdout); if (dumpBlob(txn, fs_root, entryName, entryFinalName, dirpool) == EXIT_FAILURE) return EXIT_FAILURE; } } return EXIT_SUCCESS; } -static bool wasDir(svn_fs_t *fs, int revnum, const char *pathname, apr_pool_t *pool) -{ - AprAutoPool subpool(pool); - svn_fs_root_t *fs_root; - if (svn_fs_revision_root(&fs_root, fs, revnum, subpool) != SVN_NO_ERROR) - return false; - - svn_boolean_t is_dir; - if (svn_fs_is_dir(&is_dir, fs_root, pathname, subpool) != SVN_NO_ERROR) - return false; - - return is_dir; -} - time_t get_epoch(const char* svn_date) { struct tm tm; memset(&tm, 0, sizeof tm); QByteArray date(svn_date, strlen(svn_date) - 8); strptime(date, "%Y-%m-%dT%H:%M:%S", &tm); return timegm(&tm); } class SvnRevision { public: AprAutoPool pool; QHash transactions; QList allMatchRules; RepositoryHash repositories; IdentityHash identities; QString userdomain; svn_fs_t *fs; svn_fs_root_t *fs_root; int revnum; // must call fetchRevProps first: QByteArray authorident; QByteArray log; uint epoch; bool ruledebug; bool propsFetched; bool needCommit; SvnRevision(int revision, svn_fs_t *f, apr_pool_t *parent_pool) : pool(parent_pool), fs(f), fs_root(0), revnum(revision), propsFetched(false) { ruledebug = CommandLineParser::instance()->contains( QLatin1String("debug-rules")); } int open() { SVN_ERR(svn_fs_revision_root(&fs_root, fs, revnum, pool)); return EXIT_SUCCESS; } int prepareTransactions(); int fetchRevProps(); int commit(); int exportEntry(const char *path, const svn_fs_path_change2_t *change, apr_hash_t *changes); int exportDispatch(const char *path, const svn_fs_path_change2_t *change, const char *path_from, svn_revnum_t rev_from, apr_hash_t *changes, const QString ¤t, const Rules::Match &rule, const MatchRuleList &matchRules, apr_pool_t *pool); int exportInternal(const char *path, const svn_fs_path_change2_t *change, const char *path_from, svn_revnum_t rev_from, const QString ¤t, const Rules::Match &rule, const MatchRuleList &matchRules); int recurse(const char *path, const svn_fs_path_change2_t *change, const char *path_from, const MatchRuleList &matchRules, svn_revnum_t rev_from, apr_hash_t *changes, apr_pool_t *pool); int addGitIgnore(apr_pool_t *pool, const char *key, QString path, svn_fs_root_t *fs_root, Repository::Transaction *txn, const char *content = NULL); int fetchIgnoreProps(QString *ignore, apr_pool_t *pool, const char *key, svn_fs_root_t *fs_root); int fetchUnknownProps(apr_pool_t *pool, const char *key, svn_fs_root_t *fs_root); private: void splitPathName(const Rules::Match &rule, const QString &pathName, QString *svnprefix_p, QString *repository_p, QString *effectiveRepository_p, QString *branch_p, QString *path_p); }; int SvnPrivate::exportRevision(int revnum) { SvnRevision rev(revnum, fs, global_pool); rev.allMatchRules = allMatchRules; rev.repositories = repositories; rev.identities = identities; rev.userdomain = userdomain; // open this revision: printf("Exporting revision %d ", revnum); fflush(stdout); if (rev.open() == EXIT_FAILURE) return EXIT_FAILURE; if (rev.prepareTransactions() == EXIT_FAILURE) return EXIT_FAILURE; if (!rev.needCommit) { printf(" nothing to do\n"); return EXIT_SUCCESS; // no changes? } if (rev.commit() == EXIT_FAILURE) return EXIT_FAILURE; printf(" done\n"); return EXIT_SUCCESS; } void SvnRevision::splitPathName(const Rules::Match &rule, const QString &pathName, QString *svnprefix_p, QString *repository_p, QString *effectiveRepository_p, QString *branch_p, QString *path_p) { QString svnprefix = pathName; svnprefix.truncate(rule.rx.matchedLength()); if (svnprefix_p) { *svnprefix_p = svnprefix; } if (repository_p) { *repository_p = svnprefix; repository_p->replace(rule.rx, rule.repository); foreach (Rules::Match::Substitution subst, rule.repo_substs) { subst.apply(*repository_p); } } if (effectiveRepository_p) { *effectiveRepository_p = svnprefix; effectiveRepository_p->replace(rule.rx, rule.repository); foreach (Rules::Match::Substitution subst, rule.repo_substs) { subst.apply(*effectiveRepository_p); } Repository *repository = repositories.value(*effectiveRepository_p, 0); if (repository) { *effectiveRepository_p = repository->getEffectiveRepository()->getName(); } } if (branch_p) { *branch_p = svnprefix; branch_p->replace(rule.rx, rule.branch); foreach (Rules::Match::Substitution subst, rule.branch_substs) { subst.apply(*branch_p); } } if (path_p) { QString prefix = svnprefix; prefix.replace(rule.rx, rule.prefix); *path_p = prefix + pathName.mid(svnprefix.length()); } } int SvnRevision::prepareTransactions() { // find out what was changed in this revision: apr_hash_t *changes; SVN_ERR(svn_fs_paths_changed2(&changes, fs_root, pool)); QMap map; for (apr_hash_index_t *i = apr_hash_first(pool, changes); i; i = apr_hash_next(i)) { const void *vkey; void *value; apr_hash_this(i, &vkey, NULL, &value); const char *key = reinterpret_cast(vkey); svn_fs_path_change2_t *change = reinterpret_cast(value); // If we mix path deletions with path adds/replaces we might erase a // branch after that it has been reset -> history truncated if (map.contains(QByteArray(key))) { // If the same path is deleted and added, we need to put the // deletions into the map first, then the addition. if (change->change_kind == svn_fs_path_change_delete) { // XXX } fprintf(stderr, "\nDuplicate key found in rev %d: %s\n", revnum, key); fprintf(stderr, "This needs more code to be handled, file a bug report\n"); fflush(stderr); exit(1); } map.insertMulti(QByteArray(key), change); } QMapIterator i(map); while (i.hasNext()) { i.next(); if (exportEntry(i.key(), i.value(), changes) == EXIT_FAILURE) return EXIT_FAILURE; } return EXIT_SUCCESS; } int SvnRevision::fetchRevProps() { if( propsFetched ) return EXIT_SUCCESS; apr_hash_t *revprops; SVN_ERR(svn_fs_revision_proplist(&revprops, fs, revnum, pool)); svn_string_t *svnauthor = (svn_string_t*)apr_hash_get(revprops, "svn:author", APR_HASH_KEY_STRING); svn_string_t *svndate = (svn_string_t*)apr_hash_get(revprops, "svn:date", APR_HASH_KEY_STRING); svn_string_t *svnlog = (svn_string_t*)apr_hash_get(revprops, "svn:log", APR_HASH_KEY_STRING); if (svnlog) log = svnlog->data; else log.clear(); authorident = svnauthor ? identities.value(svnauthor->data) : QByteArray(); epoch = svndate ? get_epoch(svndate->data) : 0; if (authorident.isEmpty()) { if (!svnauthor || svn_string_isempty(svnauthor)) authorident = "nobody "; else authorident = svnauthor->data + QByteArray(" <") + svnauthor->data + QByteArray("@") + userdomain.toUtf8() + QByteArray(">"); } propsFetched = true; return EXIT_SUCCESS; } int SvnRevision::commit() { // now create the commit if (fetchRevProps() != EXIT_SUCCESS) return EXIT_FAILURE; foreach (Repository *repo, repositories.values()) { repo->commit(); } foreach (Repository::Transaction *txn, transactions) { txn->setAuthor(authorident); txn->setDateTime(epoch); txn->setLog(log); - txn->commit(); + if (txn->commit() != EXIT_SUCCESS) + return EXIT_FAILURE; delete txn; } return EXIT_SUCCESS; } int SvnRevision::exportEntry(const char *key, const svn_fs_path_change2_t *change, apr_hash_t *changes) { AprAutoPool revpool(pool.data()); QString current = QString::fromUtf8(key); // was this copied from somewhere? svn_revnum_t rev_from = SVN_INVALID_REVNUM; const char *path_from = NULL; if (change->change_kind != svn_fs_path_change_delete) { // svn_fs_copied_from would fail on deleted paths, because the path // obviously no longer exists in the current revision SVN_ERR(svn_fs_copied_from(&rev_from, &path_from, fs_root, key, revpool)); } // is this a directory? svn_boolean_t is_dir; SVN_ERR(svn_fs_is_dir(&is_dir, fs_root, key, revpool)); // Adding newly created directories if (is_dir && change->change_kind == svn_fs_path_change_add && path_from == NULL && CommandLineParser::instance()->contains("empty-dirs")) { QString keyQString = key; // Skipping SVN-directory-layout if (keyQString.endsWith("/trunk") || keyQString.endsWith("/branches") || keyQString.endsWith("/tags")) { //qDebug() << "Skipping SVN-directory-layout:" << keyQString; return EXIT_SUCCESS; } needCommit = true; //qDebug() << "Adding directory:" << key; } // svn:ignore-properties - else if (is_dir && (change->change_kind == svn_fs_path_change_add || change->change_kind == svn_fs_path_change_modify) + else if (is_dir && (change->change_kind == svn_fs_path_change_add || change->change_kind == svn_fs_path_change_modify || change->change_kind == svn_fs_path_change_replace) && path_from == NULL && CommandLineParser::instance()->contains("svn-ignore")) { needCommit = true; } else if (is_dir) { if (change->change_kind == svn_fs_path_change_modify || change->change_kind == svn_fs_path_change_add) { if (path_from == NULL) { // freshly added directory, or modified properties // Git doesn't handle directories, so we don't either //qDebug() << " mkdir ignored:" << key; return EXIT_SUCCESS; } qDebug() << " " << key << "was copied from" << path_from << "rev" << rev_from; } else if (change->change_kind == svn_fs_path_change_replace) { if (path_from == NULL) qDebug() << " " << key << "was replaced"; else qDebug() << " " << key << "was replaced from" << path_from << "rev" << rev_from; } else if (change->change_kind == svn_fs_path_change_reset) { qCritical() << " " << key << "was reset, panic!"; return EXIT_FAILURE; } else { // if change_kind == delete, it shouldn't come into this arm of the 'is_dir' test qCritical() << " " << key << "has unhandled change kind " << change->change_kind << ", panic!"; return EXIT_FAILURE; } } else if (change->change_kind == svn_fs_path_change_delete) { is_dir = wasDir(fs, revnum - 1, key, revpool); } if (is_dir) current += '/'; //MultiRule: loop start //Replace all returns with continue, bool isHandled = false; foreach ( const MatchRuleList matchRules, allMatchRules ) { // find the first rule that matches this pathname MatchRuleList::ConstIterator match = findMatchRule(matchRules, revnum, current); if (match != matchRules.constEnd()) { const Rules::Match &rule = *match; if ( exportDispatch(key, change, path_from, rev_from, changes, current, rule, matchRules, revpool) == EXIT_FAILURE ) return EXIT_FAILURE; isHandled = true; } else if (is_dir && path_from != NULL) { qDebug() << current << "is a copy-with-history, auto-recursing"; if ( recurse(key, change, path_from, matchRules, rev_from, changes, revpool) == EXIT_FAILURE ) return EXIT_FAILURE; isHandled = true; } else if (is_dir && change->change_kind == svn_fs_path_change_delete) { qDebug() << current << "deleted, auto-recursing"; if ( recurse(key, change, path_from, matchRules, rev_from, changes, revpool) == EXIT_FAILURE ) return EXIT_FAILURE; isHandled = true; } } if ( isHandled ) { return EXIT_SUCCESS; } if (wasDir(fs, revnum - 1, key, revpool)) { qDebug() << current << "was a directory; ignoring"; } else if (change->change_kind == svn_fs_path_change_delete) { qDebug() << current << "is being deleted but I don't know anything about it; ignoring"; } else { qCritical() << current << "did not match any rules; cannot continue"; return EXIT_FAILURE; } return EXIT_SUCCESS; } int SvnRevision::exportDispatch(const char *key, const svn_fs_path_change2_t *change, const char *path_from, svn_revnum_t rev_from, apr_hash_t *changes, const QString ¤t, const Rules::Match &rule, const MatchRuleList &matchRules, apr_pool_t *pool) { //if(ruledebug) // qDebug() << "rev" << revnum << qPrintable(current) << "matched rule:" << rule.lineNumber << "(" << rule.rx.pattern() << ")"; switch (rule.action) { case Rules::Match::Ignore: //if(ruledebug) // qDebug() << " " << "ignoring."; return EXIT_SUCCESS; case Rules::Match::Recurse: if(ruledebug) qDebug() << "rev" << revnum << qPrintable(current) << "matched rule:" << rule.info() << " " << "recursing."; return recurse(key, change, path_from, matchRules, rev_from, changes, pool); case Rules::Match::Export: if(ruledebug) qDebug() << "rev" << revnum << qPrintable(current) << "matched rule:" << rule.info() << " " << "exporting."; if (exportInternal(key, change, path_from, rev_from, current, rule, matchRules) == EXIT_SUCCESS) return EXIT_SUCCESS; if (change->change_kind != svn_fs_path_change_delete) { if(ruledebug) qDebug() << "rev" << revnum << qPrintable(current) << "matched rule:" << rule.info() << " " << "Unable to export non path removal."; return EXIT_FAILURE; } // we know that the default action inside recurse is to recurse further or to ignore, // either of which is reasonably safe for deletion qWarning() << "WARN: deleting unknown path" << current << "; auto-recursing"; return recurse(key, change, path_from, matchRules, rev_from, changes, pool); } // never reached return EXIT_FAILURE; } int SvnRevision::exportInternal(const char *key, const svn_fs_path_change2_t *change, const char *path_from, svn_revnum_t rev_from, const QString ¤t, const Rules::Match &rule, const MatchRuleList &matchRules) { needCommit = true; QString svnprefix, repository, effectiveRepository, branch, path; splitPathName(rule, current, &svnprefix, &repository, &effectiveRepository, &branch, &path); Repository *repo = repositories.value(repository, 0); if (!repo) { if (change->change_kind != svn_fs_path_change_delete) qCritical() << "Rule" << rule << "references unknown repository" << repository; return EXIT_FAILURE; } printf("."); fflush(stdout); // qDebug() << " " << qPrintable(current) << "rev" << revnum << "->" // << qPrintable(repository) << qPrintable(branch) << qPrintable(path); if (change->change_kind == svn_fs_path_change_delete && current == svnprefix && path.isEmpty() && !repo->hasPrefix()) { if(ruledebug) qDebug() << "repository" << repository << "branch" << branch << "deleted"; return repo->deleteBranch(branch, revnum); } QString previous; QString prevsvnprefix, prevrepository, preveffectiverepository, prevbranch, prevpath; if (path_from != NULL) { previous = QString::fromUtf8(path_from); if (wasDir(fs, rev_from, path_from, pool.data())) { previous += '/'; } MatchRuleList::ConstIterator prevmatch = findMatchRule(matchRules, rev_from, previous, NoIgnoreRule); if (prevmatch != matchRules.constEnd()) { splitPathName(*prevmatch, previous, &prevsvnprefix, &prevrepository, &preveffectiverepository, &prevbranch, &prevpath); } else { qWarning() << "WARN: SVN reports a \"copy from\" @" << revnum << "from" << path_from << "@" << rev_from << "but no matching rules found! Ignoring copy, treating as a modification"; path_from = NULL; } } // current == svnprefix => we're dealing with the contents of the whole branch here if (path_from != NULL && current == svnprefix && path.isEmpty()) { if (previous != prevsvnprefix) { // source is not the whole of its branch qDebug() << qPrintable(current) << "is a partial branch of repository" << qPrintable(prevrepository) << "branch" << qPrintable(prevbranch) << "subdir" << qPrintable(prevpath); } else if (preveffectiverepository != effectiveRepository) { qWarning() << "WARN:" << qPrintable(current) << "rev" << revnum << "is a cross-repository copy (from repository" << qPrintable(prevrepository) << "branch" << qPrintable(prevbranch) << "path" << qPrintable(prevpath) << "rev" << rev_from << ")"; } else if (path != prevpath) { qDebug() << qPrintable(current) << "is a branch copy which renames base directory of all contents" << qPrintable(prevpath) << "to" << qPrintable(path); // FIXME: Handle with fast-import 'file rename' facility // ??? Might need special handling when path == / or prevpath == / } else { if (prevbranch == branch) { // same branch and same repository qDebug() << qPrintable(current) << "rev" << revnum << "is reseating branch" << qPrintable(branch) << "to an earlier revision" << qPrintable(previous) << "rev" << rev_from; } else { // same repository but not same branch // this means this is a plain branch qDebug() << qPrintable(repository) << ": branch" << qPrintable(branch) << "is branching from" << qPrintable(prevbranch); } if (repo->createBranch(branch, revnum, prevbranch, rev_from) == EXIT_FAILURE) return EXIT_FAILURE; if(CommandLineParser::instance()->contains("svn-branches")) { Repository::Transaction *txn = transactions.value(repository + branch, 0); if (!txn) { txn = repo->newTransaction(branch, svnprefix, revnum); if (!txn) return EXIT_FAILURE; transactions.insert(repository + branch, txn); } if(ruledebug) qDebug() << "Create a true SVN copy of branch (" << key << "->" << branch << path << ")"; txn->deleteFile(path); - recursiveDumpDir(txn, fs_root, key, path, pool); + recursiveDumpDir(txn, fs, fs_root, key, path, pool, revnum, rule, matchRules, ruledebug); } if (rule.annotate) { // create an annotated tag fetchRevProps(); repo->createAnnotatedTag(branch, svnprefix, revnum, authorident, epoch, log); } return EXIT_SUCCESS; } } Repository::Transaction *txn = transactions.value(repository + branch, 0); if (!txn) { txn = repo->newTransaction(branch, svnprefix, revnum); if (!txn) return EXIT_FAILURE; transactions.insert(repository + branch, txn); } // // If this path was copied from elsewhere, use it to infer _some_ // merge points. This heuristic is fairly useful for tracking // changes across directory re-organizations and wholesale branch // imports. // // NOTE(uqs): HACK ALERT! Only merge between head, projects, and user // branches for the FreeBSD repositories. Never merge into stable or // releng, as we only ever cherry-pick changes to those branches. // FIXME: Needs to move into the ruleset ... - if (path_from != NULL && preveffectiverepository == effectiveRepository && prevbranch != branch && + if (path_from != NULL && prevrepository == repository && prevbranch != branch && (branch.startsWith("master") || branch.startsWith("head") || branch.startsWith("projects") || branch.startsWith("user"))) { if(ruledebug) qDebug() << "copy from branch" << prevbranch << "to branch" << branch << "@rev" << rev_from; txn->noteCopyFromBranch (prevbranch, rev_from); } if (change->change_kind == svn_fs_path_change_replace && path_from == NULL) { if(ruledebug) qDebug() << "replaced with empty path (" << branch << path << ")"; txn->deleteFile(path); } if (change->change_kind == svn_fs_path_change_delete) { if(ruledebug) qDebug() << "delete (" << branch << path << ")"; txn->deleteFile(path); } else if (!current.endsWith('/')) { if(ruledebug) qDebug() << "add/change file (" << key << "->" << branch << path << ")"; dumpBlob(txn, fs_root, key, path, pool); } else { if(ruledebug) qDebug() << "add/change dir (" << key << "->" << branch << path << ")"; // Check unknown svn-properties - if (((path_from == NULL && change->prop_mod==1) || (path_from != NULL && change->change_kind == svn_fs_path_change_add)) + if (((path_from == NULL && change->prop_mod==1) || (path_from != NULL && (change->change_kind == svn_fs_path_change_add || change->change_kind == svn_fs_path_change_replace))) && CommandLineParser::instance()->contains("propcheck")) { if (fetchUnknownProps(pool, key, fs_root) != EXIT_SUCCESS) { qWarning() << "Error checking svn-properties (" << key << ")"; } } - int ignoreSet = false; + txn->deleteFile(path); // Add GitIgnore with svn:ignore - if (((path_from == NULL && change->prop_mod==1) || (path_from != NULL && change->change_kind == svn_fs_path_change_add)) + int ignoreSet = false; + if (((path_from == NULL && change->prop_mod==1) || (path_from != NULL && (change->change_kind == svn_fs_path_change_add || change->change_kind == svn_fs_path_change_replace))) && CommandLineParser::instance()->contains("svn-ignore")) { QString svnignore; // TODO: Check if svn:ignore or other property was changed, but always set on copy/rename (path_from != NULL) if (fetchIgnoreProps(&svnignore, pool, key, fs_root) != EXIT_SUCCESS) { qWarning() << "Error fetching svn-properties (" << key << ")"; } else if (!svnignore.isNull()) { addGitIgnore(pool, key, path, fs_root, txn, svnignore.toStdString().c_str()); ignoreSet = true; } } // Add GitIgnore for empty directories (if GitIgnore was not set previously) if (CommandLineParser::instance()->contains("empty-dirs") && ignoreSet == false) { if (addGitIgnore(pool, key, path, fs_root, txn) == EXIT_SUCCESS) { return EXIT_SUCCESS; - } else { - ignoreSet = true; } } - if (ignoreSet == false) { - txn->deleteFile(path); - } - recursiveDumpDir(txn, fs_root, key, path, pool); + recursiveDumpDir(txn, fs, fs_root, key, path, pool, revnum, rule, matchRules, ruledebug); } + if (rule.annotate) { + // create an annotated tag + fetchRevProps(); + repo->createAnnotatedTag(branch, svnprefix, revnum, authorident, + epoch, log); + } + return EXIT_SUCCESS; } int SvnRevision::recurse(const char *path, const svn_fs_path_change2_t *change, const char *path_from, const MatchRuleList &matchRules, svn_revnum_t rev_from, apr_hash_t *changes, apr_pool_t *pool) { svn_fs_root_t *fs_root = this->fs_root; if (change->change_kind == svn_fs_path_change_delete) SVN_ERR(svn_fs_revision_root(&fs_root, fs, revnum - 1, pool)); // get the dir listing svn_node_kind_t kind; SVN_ERR(svn_fs_check_path(&kind, fs_root, path, pool)); if(kind == svn_node_none) { qWarning() << "WARN: Trying to recurse using a nonexistant path" << path << ", ignoring"; return EXIT_SUCCESS; } else if(kind != svn_node_dir) { qWarning() << "WARN: Trying to recurse using a non-directory path" << path << ", ignoring"; return EXIT_SUCCESS; } apr_hash_t *entries; SVN_ERR(svn_fs_dir_entries(&entries, fs_root, path, pool)); AprAutoPool dirpool(pool); // While we get a hash, put it in a map for sorted lookup, so we can // repeat the conversions and get the same git commit hashes. QMap map; for (apr_hash_index_t *i = apr_hash_first(pool, entries); i; i = apr_hash_next(i)) { dirpool.clear(); const void *vkey; void *value; apr_hash_this(i, &vkey, NULL, &value); svn_fs_dirent_t *dirent = reinterpret_cast(value); map.insertMulti(QByteArray(dirent->name), dirent->kind); } QMapIterator i(map); while (i.hasNext()) { dirpool.clear(); i.next(); QByteArray entry = path + QByteArray("/") + i.key(); QByteArray entryFrom; if (path_from) entryFrom = path_from + QByteArray("/") + i.key(); // check if this entry is in the changelist for this revision already svn_fs_path_change2_t *otherchange = (svn_fs_path_change2_t*)apr_hash_get(changes, entry.constData(), APR_HASH_KEY_STRING); if (otherchange && otherchange->change_kind == svn_fs_path_change_add) { qDebug() << entry << "rev" << revnum << "is in the change-list, deferring to that one"; continue; } QString current = QString::fromUtf8(entry); if (i.value() == svn_node_dir) current += '/'; // find the first rule that matches this pathname MatchRuleList::ConstIterator match = findMatchRule(matchRules, revnum, current); if (match != matchRules.constEnd()) { if (exportDispatch(entry, change, entryFrom.isNull() ? 0 : entryFrom.constData(), rev_from, changes, current, *match, matchRules, dirpool) == EXIT_FAILURE) return EXIT_FAILURE; } else { if (i.value() == svn_node_dir) { qDebug() << current << "rev" << revnum << "did not match any rules; auto-recursing"; if (recurse(entry, change, entryFrom.isNull() ? 0 : entryFrom.constData(), matchRules, rev_from, changes, dirpool) == EXIT_FAILURE) return EXIT_FAILURE; } } } return EXIT_SUCCESS; } int SvnRevision::addGitIgnore(apr_pool_t *pool, const char *key, QString path, svn_fs_root_t *fs_root, Repository::Transaction *txn, const char *content) { // Check for number of subfiles if no content if (!content) { apr_hash_t *entries; SVN_ERR(svn_fs_dir_entries(&entries, fs_root, key, pool)); // Return if any subfiles if (apr_hash_count(entries)!=0) { return EXIT_FAILURE; } } // Add gitignore-File QString gitIgnorePath = path + ".gitignore"; if (content) { QIODevice *io = txn->addFile(gitIgnorePath, 33188, strlen(content)); - io->write(content); - io->putChar('\n'); + if (!CommandLineParser::instance()->contains("dry-run")) { + io->write(content); + io->putChar('\n'); + } } else { QIODevice *io = txn->addFile(gitIgnorePath, 33188, 0); - io->putChar('\n'); + if (!CommandLineParser::instance()->contains("dry-run")) { + io->putChar('\n'); + } } return EXIT_SUCCESS; } int SvnRevision::fetchIgnoreProps(QString *ignore, apr_pool_t *pool, const char *key, svn_fs_root_t *fs_root) { // Get svn:ignore svn_string_t *prop = NULL; SVN_ERR(svn_fs_node_prop(&prop, fs_root, key, "svn:ignore", pool)); if (prop) { *ignore = QString(prop->data); + // remove patterns with slashes or backslashes, + // they didn't match anything in Subversion but would in Git eventually + ignore->remove(QRegExp("^[^\\r\\n]*[\\\\/][^\\r\\n]*(?:[\\r\\n]|$)|[\\r\\n][^\\r\\n]*[\\\\/][^\\r\\n]*(?=[\\r\\n]|$)")); + // add a slash in front to have the same meaning in Git of only working on the direct children + ignore->replace(QRegExp("(^|[\\r\\n])\\s*(?![\\r\\n]|$)"), "\\1/"); } else { *ignore = QString(); } + // Get svn:global-ignores + prop = NULL; + SVN_ERR(svn_fs_node_prop(&prop, fs_root, key, "svn:global-ignores", pool)); + if (prop) { + QString global_ignore = QString(prop->data); + // remove patterns with slashes or backslashes, + // they didn't match anything in Subversion but would in Git eventually + global_ignore.remove(QRegExp("^[^\\r\\n]*[\\\\/][^\\r\\n]*(?:[\\r\\n]|$)|[\\r\\n][^\\r\\n]*[\\\\/][^\\r\\n]*(?=[\\r\\n]|$)")); + ignore->append(global_ignore); + } + + // replace multiple asterisks Subversion meaning by Git meaning + ignore->replace(QRegExp("\\*+"), "*"); + return EXIT_SUCCESS; } int SvnRevision::fetchUnknownProps(apr_pool_t *pool, const char *key, svn_fs_root_t *fs_root) { // Check all properties apr_hash_t *table; SVN_ERR(svn_fs_node_proplist(&table, fs_root, key, pool)); apr_hash_index_t *hi; void *propVal; const void *propKey; for (hi = apr_hash_first(pool, table); hi; hi = apr_hash_next(hi)) { apr_hash_this(hi, &propKey, NULL, &propVal); - if (strcmp((char*)propKey, "svn:ignore")!=0) { + if (strcmp((char*)propKey, "svn:ignore")!=0 && strcmp((char*)propKey, "svn:global-ignores")!=0 && strcmp((char*)propKey, "svn:mergeinfo") !=0) { qWarning() << "WARN: Unknown svn-property" << (char*)propKey << "set to" << ((svn_string_t*)propVal)->data << "for" << key; } } return EXIT_SUCCESS; } -