Re: [PATCH] subtree/Makefile: Standardize (esp. for packagers)
On Sun, Apr 27, 2014 at 12:35:13PM +1000, James Denholm wrote: Jeff King p...@peff.net wrote: I think the problem is that contrib/subtree does not really have an active dedicated area maintainer. Yeah, I can see how that might become a bit of a problem. I was actually thinking of doing a bit of work on subtree beyond this specific patch, so hopefully that won't be a show-stopper. We'll see what happens, I guess. Agreed. It also doesn't help that when subtree patches are proposed (especially new features instead of obvious bugs), there often seems to be little or no feedback from anyone. Depending on how much time you have: This may be outside the scope of work you were planning on, but it might be worth grepping through old mailing list archives for subtree patches that haven't been merged, and see if there is anything worth revisiting/resubmitting. I believe most of the following (at least) kind of languished and died, often with little or no real review and feedback: http://marc.info/?l=gitm=138644067726844w=2 http://marc.info/?l=gitm=138523794407181w=2 - My own series, plus another patch that has roughly the same description, but different semantics. http://marc.info/?l=gitm=136321400525507w=2 http://marc.info/?l=gitm=136321400525507w=2 - Some series from Paul Campbell. http://marc.info/?l=gitm=136122107605036w=2 http://marc.info/?l=gitm=135813589922554w=2 http://marc.info/?l=gitm=136415434127550w=2 http://marc.info/?l=gitm=136127692217856w=2 - Other series. http://marc.info/?l=gitm=138557714926045w=2 http://marc.info/?l=gitm=138129106613560w=2 http://marc.info/?l=gitm=136415882128742w=2 http://marc.info/?l=gitm=136415654228062w=2 - Miscellaneous And probably others... (I don't know if these are the latest or best versions of these, nor have I really looked at them closely to decide if they are worth including at all. Be sure to exameine not just the discussion around the specific patches, but also the other patches in each series...) - Matthew Ogilvie -- To unsubscribe from this list: send the line unsubscribe git in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 1/4] subtree: support split --rejoin --squash
On Thu, Jan 23, 2014 at 09:51:49AM +0100, Pierre Penninckx wrote: Hi again, After using the patched git-subtree (with patches 1 to 3) for a while, I suspect the added functionality does not do exactly what I wanted. So yes, now when doing a rejoin, the squash of the split commits is used. But how can I push this squash instead of the individual commits? The problem is I don't know how to reference that squashed commit. I tried adding the --branch option but it adds the branch to the top of the individual commits so no luck there. This is maybe obvious but I'm not at ease with commit references in git. Note that there are essentially two trees output by subtree --join. The first output is the main branch (with --join). With my patches and --squash, the main branch merges in a squashed representation of the subtree changes, so that the main project history doesn't have two copies of potentially tons of different commits in it's history (the original and the subtree, shown merged together). The second output is the new branch tip of the subtree itself. My patch always outputs the full history of the subtree, not a squashed representation. This is what's different from your patch, and is what I wanted. If you want this subtree output to ALSO be squashed, then it would need another option to support this. Note that there is at least one technical reason to prefer my strategy. git subtree tries to make it so you can re-run it (potentially from scratch) on the main project at any point in time, and re-generate exactly the same final subtree history, regardless of previous runs of git subtree. But if some of that history was originally squashed, it currently has no way of knowing which commits should be squashed together to properly regenerate exactly the same subtree history. This is especially true if you use --ignore-joins, which is currently the only practical workaround to the bug described in my patch 4 (about merging in history that originally branched off before the previous subtree split point). Perhaps this issue could be addressed by enhancing subtree to recognize specially-formatted squash messages, and intentionally regenerate the squashed based on them? [Side note: I think the convention on this list is to respond inline or after the previous message, not at the top, so new people can more easily pick up the discussion.] - Matthew 2014/1/23 Matthew Ogilvie mmogilvi_...@miniinfo.net: On Wed, Jan 22, 2014 at 03:58:28PM +0100, Pierre Penninckx wrote: 2013/12/7 Matthew Ogilvie mmogilvi_...@miniinfo.net Subject: [PATCH 1/4] subtree: support split --rejoin --squash Allow using --squash with git subtree split --rejoin. It will still split off (and save to --branch) the complete subtree history, but the merge done for the --rejoin will be merging a squashed representation of the new subtree commits, instead of the commits themselves (similar to how git subtree merge --squash works). Signed-off-by: Matthew Ogilvie mmogilvi_...@miniinfo.net --- I can think of a couple of possible objections to this patch. Are these (or any others) worth fixing? 1. Perhaps someone want the saved subtree (--branch) to have a squashed representation as well, as an option? Maybe we need two different --squash options? Something like --rejoin-squash? 2. It could definitely use some automated tests. In fact, pre-existing --squash functionality is hardly tested at all, either. See patch 4 comments for a script I use to help with mostly-manual testing. Sorry to bother you with this again, but I was wondering if those patches would be integrated into git anytime soon. And if not, if there is something I can do to help. I found them by the way, thanks a lot! Pierre I'm not sure when or if the patches will make it in. Junio's weekly What's cooking... email has asked for Comments? about them for the past several weeks, but I have yet to see anyone actually comment about them. Searching throught the last couple of years of mailing list archives for subtree reveals a general lack of a active maintainer(s) to help review and improve patches for git subtree. Given the general lack of help and feedback, it is understandable that Junio has largely limited inclusion of subtree patches to trivially obvious bug fixes. - Matthew Ogilvie -- To unsubscribe from this list: send the line unsubscribe git in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 1/4] subtree: support split --rejoin --squash
On Wed, Jan 22, 2014 at 03:58:28PM +0100, Pierre Penninckx wrote: 2013/12/7 Matthew Ogilvie mmogilvi_...@miniinfo.net Subject: [PATCH 1/4] subtree: support split --rejoin --squash Allow using --squash with git subtree split --rejoin. It will still split off (and save to --branch) the complete subtree history, but the merge done for the --rejoin will be merging a squashed representation of the new subtree commits, instead of the commits themselves (similar to how git subtree merge --squash works). Signed-off-by: Matthew Ogilvie mmogilvi_...@miniinfo.net --- I can think of a couple of possible objections to this patch. Are these (or any others) worth fixing? 1. Perhaps someone want the saved subtree (--branch) to have a squashed representation as well, as an option? Maybe we need two different --squash options? Something like --rejoin-squash? 2. It could definitely use some automated tests. In fact, pre-existing --squash functionality is hardly tested at all, either. See patch 4 comments for a script I use to help with mostly-manual testing. Sorry to bother you with this again, but I was wondering if those patches would be integrated into git anytime soon. And if not, if there is something I can do to help. I found them by the way, thanks a lot! Pierre I'm not sure when or if the patches will make it in. Junio's weekly What's cooking... email has asked for Comments? about them for the past several weeks, but I have yet to see anyone actually comment about them. Searching throught the last couple of years of mailing list archives for subtree reveals a general lack of a active maintainer(s) to help review and improve patches for git subtree. Given the general lack of help and feedback, it is understandable that Junio has largely limited inclusion of subtree patches to trivially obvious bug fixes. - Matthew Ogilvie -- To unsubscribe from this list: send the line unsubscribe git in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 2/4] subtree: allow --squash and --message with push
Signed-off-by: Matthew Ogilvie mmogilvi_...@miniinfo.net --- contrib/subtree/git-subtree.sh | 8 +++- contrib/subtree/git-subtree.txt | 9 - 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh index 998a9c5..56d915f 100755 --- a/contrib/subtree/git-subtree.sh +++ b/contrib/subtree/git-subtree.sh @@ -743,11 +743,17 @@ cmd_push() if [ $# -ne 2 ]; then die You must provide repository refspec fi + + opts= + if [ -n $squash ]; then + opts=-squash + fi + if [ -e $dir ]; then repository=$1 refspec=$2 echo git push using: $repository $refspec - localrev=$(git subtree split --prefix=$prefix) || die + localrev=$(git subtree split --prefix=$prefix $opts --message=$message) || die git push $repository $localrev:refs/heads/$refspec else die '$dir' must already exist. Try 'git subtree add'. diff --git a/contrib/subtree/git-subtree.txt b/contrib/subtree/git-subtree.txt index 92e7a4d..03092bc 100644 --- a/contrib/subtree/git-subtree.txt +++ b/contrib/subtree/git-subtree.txt @@ -140,20 +140,11 @@ OPTIONS want to manipulate. This option is mandatory for all commands. - -OPTIONS FOR add, merge, pull, rejoin --- -m message:: --message=message:: - This option is only valid for add, merge, pull, and - split '--rejoin'. - Specify message as the commit message for the merge commit. --squash:: - This option is only valid for add, merge, pull, and - split '--rejoin'. - Instead of merging the entire history from the subtree project, produce only a single commit that contains all the differences you want to merge, and then merge that -- 1.8.3.2 -- To unsubscribe from this list: send the line unsubscribe git in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 3/4] subtree: add --edit option
Signed-off-by: Matthew Ogilvie mmogilvi_...@miniinfo.net --- contrib/subtree/git-subtree.sh | 37 + contrib/subtree/git-subtree.txt | 4 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh index 56d915f..ac82b4d 100755 --- a/contrib/subtree/git-subtree.sh +++ b/contrib/subtree/git-subtree.sh @@ -21,6 +21,7 @@ d show debug messages P,prefix= the name of the subdir to split out m,message=use the given message as the commit message for the merge commit squashmerge subtree changes as a single commit +edit allow user to edit squash commit message interactively options for 'split' annotate= add a prefix to commit message of new commits b,branch= create a new branch from the split subtree @@ -45,6 +46,7 @@ ignore_joins= annotate= squash= message= +edit= debug() { @@ -91,6 +93,7 @@ while [ $# -gt 0 ]; do --ignore-joins) ignore_joins=1 ;; --no-ignore-joins) ignore_joins= ;; --squash) squash=1 ;; + --edit) edit=1 ;; --no-squash) squash= ;; --) break ;; *) die Unexpected option: $opt ;; @@ -434,13 +437,12 @@ new_squash_commit() old=$1 oldsub=$2 newsub=$3 + msg_file=$4 tree=$(toptree_for_commit $newsub) || exit $? if [ -n $old ]; then - squash_msg $dir $oldsub $newsub | - git commit-tree $tree -p $old || exit $? + git commit-tree $tree -p $old -F $msg_file || exit $? else - squash_msg $dir $newsub | - git commit-tree $tree || exit $? + git commit-tree $tree -F $msg_file || exit $? fi } @@ -556,7 +558,13 @@ cmd_add_commit() fi if [ -n $squash ]; then - rev=$(new_squash_commit $rev) || exit $? + msg_file=$GIT_DIR/COMMIT_EDITMSG + squash_msg $dir $rev $msg_file + if [ -n $edit ]; then + git_editor $msg_file + fi + rev=$(new_squash_commit $rev $msg_file) || exit $? + rm -f $msg_file commit=$(add_squashed_msg $rev $dir | git commit-tree $tree $headp -p $rev) || exit $? else @@ -672,8 +680,14 @@ cmd_split() say Subtree is already at commit $latest_new. exit 0 fi - new=$(new_squash_commit $old $sub $latest_new) \ - || exit $? + msg_file=$GIT_DIR/COMMIT_EDITMSG + squash_msg $dir $sub $latest_new $msg_file + if [ -n $edit ]; then + git_editor $msg_file + fi + new=$(new_squash_commit $old $sub $latest_new \ + $msg_file) || exit $? + rm -f $msg_file debug New squash commit: $new fi @@ -708,7 +722,13 @@ cmd_merge() say Subtree is already at commit $rev. exit 0 fi - new=$(new_squash_commit $old $sub $rev) || exit $? + msg_file=$GIT_DIR/COMMIT_EDITMSG + squash_msg $dir $sub $rev $msg_file + if [ -n $edit ]; then + git_editor $msg_file + fi + new=$(new_squash_commit $old $sub $rev $msg_file) || exit $? + rm -f $msg_file debug New squash commit: $new rev=$new fi @@ -748,6 +768,7 @@ cmd_push() if [ -n $squash ]; then opts=-squash fi + # Can't easily pass on --edit because of stdout capture redirection if [ -e $dir ]; then repository=$1 diff --git a/contrib/subtree/git-subtree.txt b/contrib/subtree/git-subtree.txt index 03092bc..16525d4 100644 --- a/contrib/subtree/git-subtree.txt +++ b/contrib/subtree/git-subtree.txt @@ -177,6 +177,10 @@ OPTIONS the merge back to the mainline, not the synthetic subtree history. +--edit:: + When used with '--squash', bring up an editor on the squash + commit message, to allow customizing it. + OPTIONS FOR split - -- 1.8.3.2 -- To unsubscribe from this list: send the line unsubscribe git in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 1/4] subtree: support split --rejoin --squash
Allow using --squash with git subtree split --rejoin. It will still split off (and save to --branch) the complete subtree history, but the merge done for the --rejoin will be merging a squashed representation of the new subtree commits, instead of the commits themselves (similar to how git subtree merge --squash works). Signed-off-by: Matthew Ogilvie mmogilvi_...@miniinfo.net --- I can think of a couple of possible objections to this patch. Are these (or any others) worth fixing? 1. Perhaps someone want the saved subtree (--branch) to have a squashed representation as well, as an option? Maybe we need two different --squash options? Something like --rejoin-squash? 2. It could definitely use some automated tests. In fact, pre-existing --squash functionality is hardly tested at all, either. See patch 4 comments for a script I use to help with mostly-manual testing. contrib/subtree/git-subtree.sh | 60 +++-- contrib/subtree/git-subtree.txt | 27 --- 2 files changed, 63 insertions(+), 24 deletions(-) diff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh index 7d7af03..998a9c5 100755 --- a/contrib/subtree/git-subtree.sh +++ b/contrib/subtree/git-subtree.sh @@ -20,14 +20,13 @@ q quiet d show debug messages P,prefix= the name of the subdir to split out m,message=use the given message as the commit message for the merge commit +squashmerge subtree changes as a single commit options for 'split' annotate= add a prefix to commit message of new commits b,branch= create a new branch from the split subtree ignore-joins ignore prior --rejoin commits onto= try connecting new tree to an existing one rejoinmerge the new branch back into HEAD - options for 'add', 'merge', 'pull' and 'push' -squashmerge subtree changes as a single commit eval $(echo $OPTS_SPEC | git rev-parse --parseopt -- $@ || echo exit $?) @@ -229,13 +228,19 @@ find_latest_squash() sq= main= sub= + par1= + par2= git log --grep=^git-subtree-dir: $dir/*\$ \ - --pretty=format:'START %H%n%s%n%n%b%nEND%n' HEAD | - while read a b junk; do - debug $a $b $junk + --pretty=format:'START %H %P%n%s%n%n%b%nEND%n' HEAD | + while read a b c d junk; do + debug $a $b $c $d $junk debug {{$sq/$main/$sub}} case $a in - START) sq=$b ;; + START) + sq=$b + par1=$c + par2=$d + ;; git-subtree-mainline:) main=$b ;; git-subtree-split:) sub=$b ;; END) @@ -243,7 +248,8 @@ find_latest_squash() if [ -n $main ]; then # a rejoin commit? # Pretend its sub was a squash. - sq=$sub + assert [ $main = $par1 ] + sq=$par2 fi debug Squash found: $sq $sub echo $sq $sub @@ -252,6 +258,8 @@ find_latest_squash() sq= main= sub= + par1= + par2= ;; esac done @@ -565,6 +573,13 @@ cmd_split() debug Splitting $dir... cache_setup || exit $? + if [ -n $rejoin ]; then + ensure_clean + if [ -n $squash ]; then + first_split=$(find_latest_squash $dir) + fi + fi + if [ -n $onto ]; then debug Reading history for --onto=$onto... git rev-list $onto | @@ -630,13 +645,6 @@ cmd_split() die No new revisions were found fi - if [ -n $rejoin ]; then - debug Merging split branch into HEAD... - latest_old=$(cache_get latest_old) - git merge -s ours \ - -m $(rejoin_msg $dir $latest_old $latest_new) \ - $latest_new 2 || exit $? - fi if [ -n $branch ]; then if rev_exists refs/heads/$branch; then if ! rev_is_descendant_of_branch $latest_new $branch; then @@ -649,6 +657,30 @@ cmd_split() git update-ref -m 'subtree split' refs/heads/$branch $latest_new || exit $? say $action branch '$branch' fi + if [ -n
[PATCH/BAD 4/4] subtree: poor bugfix for split new commits with parents before previous split
Bug description: Unless you use --ignore-joins, git subtree split's optimization to avoid re-scanning all of history can trim too much. Any new merged branches that have parents before the previous split will not be re-attached properly in the split-off subtree. In the extreme case (if all the changes occurred on such branches), I've seen the old subtree head might not be an ancestor of the new head at all. This fix is only for illustration. It probably should not be included as-is; it probably scales worse than using --ignore-joins for anything much beyond trivial cases. I'm not sure it is possible to speed it up much (while remaining correct) without switching to a better language than shell script. I'm not sure how to explain the constraint clearly (or even precisely define the minimal constraint). One attempt: Exclude from unrevs any rejoin commit X for which ANY new commit (not just revs itself) has some other common ancestor (merge base) besides X. (New commit is one that is not an ancestor of some previous rejoin commit.) It would probably be fairly straightforward to adapt graph traversal algorithms to do this efficiently, but as near as I can tell, none of the existing options in git-rev-list or git-merge-base really does anything useful for optimizing this in shell script... The simplest traversal technique to fix this might be if there was a way to make history traversal only stop at SPECIFIC unrevs, instead of any ancestor of any unrevs. Other workarounds besides this patch: * Use --ignore-joins and wait for the really slow processing... * Plan ahead and delay: After using git subtree split --rejoin, leave the rejoin merge commit off on a side branch until such time that all other pre-split branches have been merged. Then merge the merge at that later time. Only do rejoins fairly rarely, based on the schedule of merging other branches. * Ignore the problem: Allow the occasional falsely-disconnected branch roots to be generated by split. Of course, this means --ignore-joins would no longer generate a consistent subtree history, it is hard to examine what actually changed in the false roots, etc. * Perhaps manually use temporary grafts or similar to hide rejoins that would throw it off when doing later splits. [Intentionally not signed off: Poor performance.] --- Testing: Below I include a script I've been using to help with mostly-manual testing. I regularly modify it based on what I'm testing at the moment. It basically creates and manipulates a subtree out of git's own contrib directory. It may be convenient to use this on a copy of git's repository instead of the copy in which you are messing with git-subtree (avoiding changing code out from under the script). It may also be convenient to symlink git-subtree to the top-level directory, rather than copy it, so you don't need to keep re-copying it. --CUT-- #!/bin/sh die() { echo $@ 12 exit 1 } GIT=../git-sandbox/bin-wrappers/git #GIT=git DBG= #DBG=-d EDIT= #EDIT=--edit git branch -D br-contrib git checkout 73bbc0796b4ce65bfb1a12b47a0edc27845ecf50 || die checkout $GIT subtree split -P contrib \ --branch br-contrib --squash --rejoin $EDIT || die subtree 1 git tag -f step1 git merge -m 'MY MERGE1' 68a65f5fe54c2b21bfe16ef3a0b48956ecf5658a || die merge 1 $GIT subtree split -P contrib \ --branch br-contrib --squash --rejoin || die subtree 2 git tag -f step2 git merge -m 'MY MERGE2' 15f7221686eac053902b906c278680b485c865ce || \ die merge 2 $GIT subtree split -P contrib \ --branch br-contrib --squash --rejoin $EDIT || die subtree 3 # if [ -n $EDIT ]; then exit 0 fi git tag -f step3 git merge -m 'MY MERGE3' 26145c9c73ed51bbd8261949d31899d6507519d5 || \ die merge 3 $GIT subtree split -P contrib \ $DBG --branch br-contrib --squash --rejoin || die subtree 4 git tag -f step4 git merge -m 'MY MERGE4' 583736c0bcf09adaa5621b142d8e43c22354041b || \ die merge 4 $GIT subtree split -P contrib \ --branch br-contrib --squash --rejoin || die subtree 5 git tag -f step5 --CUT-- contrib/subtree/git-subtree.sh | 61 +- 1 file changed, 55 insertions(+), 6 deletions(-) diff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh index ac82b4d..6ff4362 100755 --- a/contrib/subtree/git-subtree.sh +++ b/contrib/subtree/git-subtree.sh @@ -36,6 +36,8 @@ PATH=$PATH:$(git --exec-path) require_work_tree +nl=' +' quiet= branch= debug= @@ -224,6 +226,13 @@ try_remove_previous() fi } +try_remove() +{ + if rev_exists $1; then + echo $1 + fi +} + find_latest_squash() { debug Looking for latest squash ($dir)... @@ -293,8 +302,8 @@ find_existing_splits() debug Prior: $main - $sub cache_set $main
Re: [PATCH] subtree: add squash handling for split and push
On Sat, Nov 23, 2013 at 09:18:56PM +0100, Pierre Penninckx wrote: The documentation of subtree says that the --squash option can be used for add, merge, split and push subtree commands but only add and merge is implemented. Clarification: The current documentation (correctly) doesn't actually claim to support split --squash, but it does erroneously claim to support push --squash. cmd_split() first lets split do it's job: finding which commits need to be extracted. Now we remember which commit is the parent of the first extracted commit. When this step is done, cmd_split() generates a squash of the new commits, starting from the aforementioned parent to the last extracted commit. This new commit's sha1 is then used for the rest of the script. I've been planning to implement something similar to this patch, but the semantics I am aiming at are slightly different. It looks like your patch is basically squashing the new subtree commits together, throwing out those commits completely, and only keeping the squashed commit in the split --branch. I intend to implement slightly different semantics, where --squash only affects --rejoin, not the printed commit nor the split-off --branch. This is intended to provide a better, third option for --rejoin'ing a subtree with a lot of history, while preserving history in the split-off branch: 1. (existing/slow) Don't ever use --rejoin at all? You can use merge --squash to merge in unrelated changes to the split-off project, but every split still gets slower and slower as each split needs to re-sift-through all the same history the previous splits have sifted through. 2. (existing/huge mass of duplicated history) Use split --rejoin occasionally. This pulls in the entire history of the subtree branch (since the last --rejoin or non-squash merge, or everything if neither has been done), which is difficult to ignore when looking at global history of the full project, especially if it is many pages of commits. But subsequent splits can stop history traversal at the known-common point, and will run MUCH faster. 3. (new/better) Use split --rejoin --squash (or some other invocation to be defined). The subtree branch is generated exactly like normal, including fine-grained history. But instead of merging the subtree branch directly, --rejoin will squash all the changes to that branch, and merge in just the squash (referencing the unsquashed split branch tip in the commit message, but not the parent). Subsequent splits can run very fast, while the --rejoin only generated two commits instead of the potentially thousands of (mostly) duplicates it would pull in without the --squash. I have this third option half-coded already, but I still need to finish it. I'm fairly sure I can make this work without new adverse effects, but if someone sees something I'm missing, let me know. Does anyone have any suggestions about the UI? Do we need to also support Pierre Penninckx's split --squash semantics somehow? If so, what command line options would allow for distinguishing the two cases? -- Matthew Ogilvie [mmogilvi_...@miniinfo.net] -- To unsubscribe from this list: send the line unsubscribe git in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 09/20] cvsserver: cvs add: do not expand directory arguments
Standard cvs add never does any recursion. With standard cvs, cvs add dir will either add just the dir to the repository, or error out. Prior to this change, git-cvsserver would try to recurse (perhaps re-adding sandbox-removed files?) into the existing directory instead. Signed-off-by: Matthew Ogilvie mmogilvi_...@miniinfo.net --- git-cvsserver.perl | 2 -- 1 file changed, 2 deletions(-) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 8a7106d..4d514b4 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -543,8 +543,6 @@ sub req_add my $updater = GITCVS::updater-new($state-{CVSROOT}, $state-{module}, $log); $updater-update(); -argsfromdir($updater); - my $addcount = 0; foreach my $filename ( @{$state-{args}} ) -- 1.7.10.2.484.gcd07cc5 -- To unsubscribe from this list: send the line unsubscribe git in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 18/20] cvsserver: support -r and sticky tags for most operations
- Split off prepDirForOutput for update and commit. Some low level protocol details were changed to more closely resemble CVS even in non-tagged cases. Hopefully it still works with finicky clients like Eclipse. - Substantial changes to diff. The output is now closer to standard CVS (including exit status), and can be used as a patch, but there are still a number of differences compared to CVS. - Tweaks to add, remove, status, and commit. - FUTURE: CVS revision numbers for branches simply encode git commit IDs in a way that resembles CVS revision numbers, dropping all normal CVS structural relations between different revision numbers. - FUTURE: log doesn't try to work properly at all with branches and tags. - FUTURE: annotate probably doesn't work with branches or tags either (untested)? Signed-off-by: Matthew Ogilvie mmogilvi_...@miniinfo.net --- git-cvsserver.perl | 713 ++--- 1 file changed, 510 insertions(+), 203 deletions(-) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 5e558d1..3679074 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -611,7 +611,10 @@ sub req_add { $filename = filecleanup($filename); -my $meta = $updater-getmeta($filename); +# no -r, -A, or -D with add +my $stickyInfo = resolveStickyInfo($filename); + +my $meta = $updater-getmeta($filename,$stickyInfo); my $wrev = revparse($filename); if ($wrev $meta ($wrev=~/^-/)) @@ -634,8 +637,10 @@ sub req_add # this is an entries line my $kopts = kopts_from_path($filename,sha1,$meta-{filehash}); -$log-debug(/$filepart/$meta-{revision}//$kopts/); -print /$filepart/$meta-{revision}//$kopts/\n; +my $entryLine = /$filepart/$meta-{revision}//$kopts/; +$entryLine .= getStickyTagOrDate($stickyInfo); +$log-debug($entryLine); +print $entryLine\n; # permissions $log-debug(SEND : u=$meta-{mode},g=$meta-{mode},o=$meta-{mode}); print u=$meta-{mode},g=$meta-{mode},o=$meta-{mode}\n; @@ -666,7 +671,8 @@ sub req_add print $filename\n; my $kopts = kopts_from_path($filename,file, $state-{entries}{$filename}{modified_filename}); -print /$filepart/0//$kopts/\n; +print /$filepart/0//$kopts/ . + getStickyTagOrDate($stickyInfo) . \n; my $requestedKopts = $state-{opt}{k}; if(defined($requestedKopts)) @@ -734,7 +740,10 @@ sub req_remove next; } -my $meta = $updater-getmeta($filename); +# only from entries +my $stickyInfo = resolveStickyInfo($filename); + +my $meta = $updater-getmeta($filename,$stickyInfo); my $wrev = revparse($filename); unless ( defined ( $wrev ) ) @@ -764,7 +773,7 @@ sub req_remove print Checked-in $dirpart\n; print $filename\n; my $kopts = kopts_from_path($filename,sha1,$meta-{filehash}); -print /$filepart/-$wrev//$kopts/\n; +print /$filepart/-$wrev//$kopts/ . getStickyTagOrDate($stickyInfo) . \n; $rmcount++; } @@ -944,6 +953,9 @@ sub req_co return 1; } +my $stickyInfo = { 'tag' = $state-{opt}{r}, + 'date' = $state-{opt}{D} }; + my $module = $state-{args}[0]; $state-{module} = $module; my $checkout_path = $module; @@ -961,64 +973,32 @@ sub req_co my $updater = GITCVS::updater-new($state-{CVSROOT}, $module, $log); $updater-update(); -$checkout_path =~ s|/$||; # get rid of trailing slashes +my $headHash; +if( defined($stickyInfo) defined($stickyInfo-{tag}) ) +{ +$headHash = $updater-lookupCommitRef($stickyInfo-{tag}); +if( !defined($headHash) ) +{ +print error 1 no such tag `$stickyInfo-{tag}'\n; +cleanupWorkTree(); +exit; +} +} -# Eclipse seems to need the Clear-sticky command -# to prepare the 'Entries' file for the new directory. -print Clear-sticky $checkout_path/\n; -print $state-{CVSROOT} . /$module/\n; -print Clear-static-directory $checkout_path/\n; -print $state-{CVSROOT} . /$module/\n; -print Clear-sticky $checkout_path/\n; # yes, twice -print $state-{CVSROOT} . /$module/\n; -print Template $checkout_path/\n; -print $state-{CVSROOT} . /$module/\n; -print 0\n; - -# instruct the client that we're checking out to $checkout_path -print E cvs checkout: Updating $checkout_path\n; +$checkout_path =~ s|/$||; # get rid of trailing slashes my %seendirs = (); my $lastdir =''; -# recursive -sub prepdir { - my ($dir, $repodir, $remotedir, $seendirs) = @_; - my $parent = dirname($dir); - $dir
[PATCH 00/20] git-cvsserver: add support for cvs -r refs
This patch series improves git-cvsserver's handling and support of refspecs requested by the client. Disclaimer: I don't actually intend to use any of this myself. I suspect no one really cares about cvsserver (I don't even really care myself). See also below. --- Features: --- 1. You can now switch branches with cvs update -r REFSPEC, cvs update -A, cvs checkout -r ..., etc. It becomes sticky for plain cvs update until reset (like standard CVS). You can add/remove/commit to other branches updated this way. The old cvs checkout moduleThatIsReallyABranch technique still works; it becomes the main head branch from CVS's perspective. CVS revision number allocation is not synchronized between different modules checkout out in this old way. 2. You can create arbitrary diffs and patches with commands such as cvs diff -r REFSPEC [-r REFSPEC], over files, directories, or whole trees. (It used to effectively be limited to individual files, and CVS-specific revision numbers (no tags).) 3. The REFSPECs can refer to CVS revision numbers, git commit IDs, git tags, git branches, or other git refspecs. There is an escape mechanism you can use if your CVS client won't let you feed in invalid CVS tags that contain ., /, or similar characters. 4. No DB schema changes for now. You can downgrade back to previous versions, and the only things that may break are existing sandboxes that were explicitly using the new features (had been set to a sticky branch with cvs update -r). --- Limitations: --- 1. CVS revision numbers (other than those on the main branch) are not real. Instead, they are git commit ID's encoded to look like valid CVS revision numbers. Subsequent versions do not have related CVS revision numbers at all. (Most of the code in this patch series would probably still be useful if someone enhances CVS revision numbers in the future.) 2. CVS log does not attempt to show versions on other than the main branch, even if a non-main branch is currently checked out. It also doesn't show any branch or tag names. 3. Performance: When updating, it attempts to find a main-branch CVS revision number, or failing that, restrict itself to the specific git commit ID where the file most recently changed. Searching for these (on an individual file basis) can currently be rather expensive, and probably doesn't scale well. There are several possible ways it might be optimized, but no optimization has been done. See comments in the patch. 4. I've done almost no testing, other than running both old and new unit tests (t9400, t9401, and t9402). 5. Testing Eclipse: I needed to make some changes to the details of what cvs update sends for the CVS protocol, even in the non-branch case. I think I've made it closer to being the same as a standard CVS server (based on some captured conversations), but it still isn't perfect. With luck, reputedly-finicky clients like Eclipse will still work correctly, but I haven't actually verified this. 6. Pre-existing issue: There are cases where the server will ask the client to replace user-modified files without saving those modifications off to the side first (.# files). I haven't fixed these, but the fourth patch adds a comment about one such case. 7. Coding style: git-cvsserver mostly doesn't use git's main coding style. In fact, different parts of the file seem to use different coding styles. I've made a superficial attempt to have each change keep relatively close to nearby coding style. But larger chunks of code tend to stick to a hybrid of what seems most predominant in the file as a whole, and my own personally preferred style. --- Disclaimer: --- I don't actually intend to use any of this myself. I started it a few years ago under the theory that a more accurate emulation of CVS would make it easier to convince the team at $DAYJOB to switch to git, but we eventually switched without using git-cvsserver at all. I've been working on this on and off (mostly off) out of a vague sense of stubborness. Depending on overall interest in this feature (most likely not much) vs how many adjustments maintainers want, I might gradually get this into a ready-to-include state (possibly weeks or months). But my primary goal here is just to have it available publicly somewhere (like the mailing list) where someone who really wants features like these can use these patches as a starting point. That said, perhaps some of the trivial cleanup patches could go in now? --- Matthew Ogilvie (20): cvsserver t9400: add basic 'cvs log' test cvsserver: removed unused sha1Or-k mode from kopts_from_path cvsserver: add comments about database schema/usage cvsserver update: comment about how we shouldn't remove a user-modified file cvsserver: remove unused functions _headrev
[PATCH 01/20] cvsserver t9400: add basic 'cvs log' test
'cvs log' output is arguably deficient in a number of ways (see the comment added with the test), but add a test for the current output to detect for accidental regressions. Signed-off-by: Matthew Ogilvie mmogilvi_...@miniinfo.net --- t/t9400-git-cvsserver-server.sh | 70 + 1 file changed, 70 insertions(+) diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh index 806623e..6c693ff 100755 --- a/t/t9400-git-cvsserver-server.sh +++ b/t/t9400-git-cvsserver-server.sh @@ -505,6 +505,76 @@ test_expect_success 'cvs co -c (shows module database)' ' ' # +# CVS LOG +# + +# Known issues with git-cvsserver current log output: +# - Hard coded lines: +2 -3 placeholder, instead of real numbers. +# - CVS normally does not internally add a blank first line +#nor a last line with nothing but a space to log messages. +# - The latest cvs 1.12.x server sends + timezone (with some hidden MT +#tagging in the protocol), and if cvs 1.12.x client sees the MT tags, +#it converts to local time zone. git-cvsserver doesn't do the + +#or the MT tags... +# - The latest 1.12.x releases add a commitid: field on to the end of the +#date: line (after lines:). Maybe we could stick git's commit id +#in it? Or does CVS expect a certain number of bits (too few for +#a full sha1)? +# +# Given the above, expect the following test to break if git-cvsserver's +# log output is improved. The test is just to ensure it doesn't +# accidentally get worse. + +sed -e 's/^x//' -e 's/SP$/ /' $WORKDIR/expect EOF +x +xRCS file: $WORKDIR/gitcvs.git/master/merge,v +xWorking file: merge +xhead: 1.4 +xbranch: +xlocks: strict +xaccess list: +xsymbolic names: +xkeyword substitution: kv +xtotal revisions: 4; selected revisions: 4 +xdescription: +x +xrevision 1.4 +xdate: __DATE__; author: author; state: Exp; lines: +2 -3 +x +xMerge test (no-op) +xSP +x +xrevision 1.3 +xdate: __DATE__; author: author; state: Exp; lines: +2 -3 +x +xMerge test (conflict) +xSP +x +xrevision 1.2 +xdate: __DATE__; author: author; state: Exp; lines: +2 -3 +x +xMerge test (merge) +xSP +x +xrevision 1.1 +xdate: __DATE__; author: author; state: Exp; lines: +2 -3 +x +xMerge test (pre-merge) +xSP +x= +EOF +expectStat=$? + +cd $WORKDIR +test_expect_success 'cvs log' ' +cd cvswork +test x$expectStat = x0 +GIT_CONFIG=$git_config cvs log merge ../out +sed -e s%2[0-9][0-9][0-9]/[01][0-9]/[0-3][0-9] [0-2][0-9]:[0-5][0-9]:[0-5][0-9]%__DATE__% ../out ../actual +test_cmp ../expect ../actual +' + +# # CVS ANNOTATE # -- 1.7.10.2.484.gcd07cc5 -- To unsubscribe from this list: send the line unsubscribe git in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 06/20] cvsserver: clean up client request handler map comments
- Comment that it should not be considered a complete list. - #'annotate' comment - Uncommented annotate line is 2 lines earlier. Signed-off-by: Matthew Ogilvie mmogilvi_...@miniinfo.net --- git-cvsserver.perl | 5 - 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 8b2accb..9e71f30 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -51,6 +51,10 @@ $| = 1; Definition and mappings of functions +# NOTE: Despite the existence of req_CATCHALL and req_EMPTY unimplemented +# requests, this list is incomplete. It is missing many rarer/optional +# requests. Perhaps some clients require a claim of support for +# these specific requests for main functionality to work? my $methods = { 'Root'= \req_Root, 'Valid-responses' = \req_Validresponses, @@ -80,7 +84,6 @@ my $methods = { 'noop'= \req_EMPTY, 'annotate'= \req_annotate, 'Global_option' = \req_Globaloption, -#'annotate'= \req_CATCHALL, }; ## -- 1.7.10.2.484.gcd07cc5 -- To unsubscribe from this list: send the line unsubscribe git in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 04/20] cvsserver update: comment about how we shouldn't remove a user-modified file
Instead of a comment, we should really add test cases and actually fix it. Signed-off-by: Matthew Ogilvie mmogilvi_...@miniinfo.net --- git-cvsserver.perl | 4 1 file changed, 4 insertions(+) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 595865c..cc39b6b 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -1144,6 +1144,10 @@ sub req_update if ( $meta-{filehash} eq deleted ) { +# TODO: If it has been modified in the sandbox, error out +# with the appropriate message, rather than deleting a modified +# file. + my ( $filepart, $dirpart ) = filenamesplit($filename,1); $log-info(Removing '$filename' from working copy (no longer in the repo)); -- 1.7.10.2.484.gcd07cc5 -- To unsubscribe from this list: send the line unsubscribe git in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 08/20] cvsserver: use whole CVS rev number in-process; don't strip 1. prefix
Keep track of the whole CVS revision number in-process. This will clarify code when we start handling non-linear revision numbers later. There is one externally visible change: conflict markers after an update will now include the full CVS revision number, including the 1. prefix. It used to leave off the prefix. Other than the conflict marker, this change doesn't effect external functionality. No new features, and the DB schema is unchanged such that it continues to store just the stripped rev numbers (without prefix). Signed-off-by: Matthew Ogilvie mmogilvi_...@miniinfo.net --- git-cvsserver.perl | 225 ++-- t/t9400-git-cvsserver-server.sh | 2 +- 2 files changed, 123 insertions(+), 104 deletions(-) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index eb8f138..8a7106d 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -554,10 +554,10 @@ sub req_add my $meta = $updater-getmeta($filename); my $wrev = revparse($filename); -if ($wrev $meta ($wrev 0)) +if ($wrev $meta ($wrev=~/^-/)) { # previously removed file, add back -$log-info(added file $filename was previously removed, send 1.$meta-{revision}); +$log-info(added file $filename was previously removed, send $meta-{revision}); print MT +updated\n; print MT text U \n; @@ -574,8 +574,8 @@ sub req_add # this is an entries line my $kopts = kopts_from_path($filename,sha1,$meta-{filehash}); -$log-debug(/$filepart/1.$meta-{revision}//$kopts/); -print /$filepart/1.$meta-{revision}//$kopts/\n; +$log-debug(/$filepart/$meta-{revision}//$kopts/); +print /$filepart/$meta-{revision}//$kopts/\n; # permissions $log-debug(SEND : u=$meta-{mode},g=$meta-{mode},o=$meta-{mode}); print u=$meta-{mode},g=$meta-{mode},o=$meta-{mode}\n; @@ -683,13 +683,13 @@ sub req_remove next; } -if ( defined($wrev) and $wrev 0 ) +if ( defined($wrev) and ($wrev=~/^-/) ) { print E cvs remove: file `$filename' already scheduled for removal\n; next; } -unless ( $wrev == $meta-{revision} ) +unless ( $wrev eq $meta-{revision} ) { # TODO : not sure if the format of this message is quite correct. print E cvs remove: Up to date check failed for `$filename'\n; @@ -704,7 +704,7 @@ sub req_remove print Checked-in $dirpart\n; print $filename\n; my $kopts = kopts_from_path($filename,sha1,$meta-{filehash}); -print /$filepart/-1.$wrev//$kopts/\n; +print /$filepart/-$wrev//$kopts/\n; $rmcount++; } @@ -995,7 +995,7 @@ sub req_co # this is an entries line my $kopts = kopts_from_path($fullName,sha1,$git-{filehash}); -print /$git-{name}/1.$git-{revision}//$kopts/\n; +print /$git-{name}/$git-{revision}//$kopts/\n; # permissions print u=$git-{mode},g=$git-{mode},o=$git-{mode}\n; @@ -1083,7 +1083,7 @@ sub req_update } my $meta; -if ( defined($state-{opt}{r}) and $state-{opt}{r} =~ /^1\.(\d+)/ ) +if ( defined($state-{opt}{r}) and $state-{opt}{r} =~ /^(1\.\d+)$/ ) { $meta = $updater-getmeta($filename, $1); } else { @@ -1105,7 +1105,7 @@ sub req_update { $meta = { name = $filename, - revision = 0, + revision = '0', filehash = 'added' }; } @@ -1115,7 +1115,7 @@ sub req_update my $wrev = revparse($filename); # If the working copy is an old revision, lets get that version too for comparison. -if ( defined($wrev) and $wrev != $meta-{revision} ) +if ( defined($wrev) and $wrev ne $meta-{revision} ) { $oldmeta = $updater-getmeta($filename, $wrev); } @@ -1126,7 +1126,7 @@ sub req_update # and the working copy is unmodified _and_ the user hasn't specified -C next if ( defined ( $wrev ) and defined($meta-{revision}) - and $wrev == $meta-{revision} + and $wrev eq $meta-{revision} and $state-{entries}{$filename}{unchanged} and not exists ( $state-{opt}{C} ) ); @@ -1134,7 +1134,7 @@ sub req_update # but the working copy is modified, tell the client it's modified if ( defined ( $wrev ) and defined($meta-{revision}) - and $wrev == $meta-{revision} + and $wrev eq $meta-{revision} and defined($state-{entries}{$filename}{modified_hash}) and not exists ( $state-{opt}{C} ) ) { @@ -1168,7 +1168,7 @@ sub req_update
[PATCH 14/20] cvsserver: add misc commit lookup, file meta data, and file listing functions
These will be used soon, but not yet. PERFORMANCE NOTE: getMetaFromCommithash() does not scale well as currently implemented. See comment for possible optimization strategies. Fortunately, it will only be used in cases that would not have worked at all before this change. Signed-off-by: Matthew Ogilvie mmogilvi_...@miniinfo.net --- git-cvsserver.perl | 366 - 1 file changed, 365 insertions(+), 1 deletion(-) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 4eecc0b..16e2e1f 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -2986,6 +2986,9 @@ sub new die Git repo '$self-{git_path}' doesn't exist unless ( -d $self-{git_path} ); +# Stores full sha1's for various branch/tag names, abbreviations, etc: +$self-{commitRefCache} = {}; + $self-{dbdriver} = $cfg-{gitcvs}{$state-{method}}{dbdriver} || $cfg-{gitcvs}{dbdriver} || SQLite; $self-{dbname} = $cfg-{gitcvs}{$state-{method}}{dbname} || @@ -3446,7 +3449,7 @@ sub update ); } # invalidate the gethead cache -$self-{gethead_cache} = undef; +$self-clearCommitRefCaches(); # Ending exclusive lock here @@ -3648,6 +3651,169 @@ sub gethead return $tree; } +=head2 getAnyHead + +Returns a reference to an array of getmeta structures, one +per file in the specified tree hash. + +=cut + +sub getAnyHead +{ +my ($self,$hash) = @_; + +if(!defined($hash)) +{ +return $self-gethead(); +} + +my @files; +{ +open(my $filePipe, '-|', 'git', 'ls-tree', '-z', '-r', $hash) +or die(Cannot call git-ls-tree : $!); +local $/ = \0; +@files=$filePipe; +close $filePipe; +} + +my $tree=[]; +my($line); +foreach $line (@files) +{ +$line=~s/\0$//; +unless ( $line=~/^(\d+)\s+(\w+)\s+([a-zA-Z0-9]+)\t(.*)$/o ) +{ +die(Couldn't process git-ls-tree line : $_); +} + +my($mode, $git_type, $git_hash, $git_filename) = ($1, $2, $3, $4); +push @$tree, $self-getMetaFromCommithash($git_filename,$hash); +} + +return $tree; +} + +=head2 getRevisionDirMap + +A revision dir map contains all the plain-file filenames associated +with a particular revision (treeish), organized by directory: + + $type = $out-{$dir}{$fullName} + +The type of each is F (for ordinary file) or D (for directory, +for which the map $out-{$fullName} will also exist). + +=cut + +sub getRevisionDirMap +{ +my ($self,$ver)=@_; + +if(!defined($self-{revisionDirMapCache})) +{ +$self-{revisionDirMapCache}={}; +} + +# Get file list (previously cached results are dependent on HEAD, +# but are early in each case): +my $cacheKey; +my (@fileList); +if( !defined($ver) || $ver eq ) +{ +$cacheKey=; +if( defined($self-{revisionDirMapCache}{$cacheKey}) ) +{ +return $self-{revisionDirMapCache}{$cacheKey}; +} + +my @head = @{$self-gethead()}; +foreach my $file ( @head ) +{ +next if ( $file-{filehash} eq deleted ); + +push @fileList,$file-{name}; +} +} +else +{ +my ($hash)=$self-lookupCommitRef($ver); +if( !defined($hash) ) +{ +return undef; +} + +$cacheKey=$hash; +if( defined($self-{revisionDirMapCache}{$cacheKey}) ) +{ +return $self-{revisionDirMapCache}{$cacheKey}; +} + +open(my $filePipe, '-|', 'git', 'ls-tree', '-z', '-r', $hash) +or die(Cannot call git-ls-tree : $!); +local $/ = \0; +while ( $filePipe ) +{ +chomp; +unless ( /^(\d+)\s+(\w+)\s+([a-zA-Z0-9]+)\t(.*)$/o ) +{ +die(Couldn't process git-ls-tree line : $_); +} + +my($mode, $git_type, $git_hash, $git_filename) = ($1, $2, $3, $4); + +push @fileList, $git_filename; +} +close $filePipe; +} + +# Convert to normalized form: +my %revMap; +my $file; +foreach $file (@fileList) +{ +my($dir) = ($file=~m%^(?:(.*)/)?([^/]*)$%); +$dir='' if(!defined($dir)); + +# parent directories: +# ... create empty dir maps for parent dirs: +my($td)=$dir; +while(!defined($revMap{$td})) +{ +$revMap{$td}={}; + +my($tp)=($td=~m%^(?:(.*)/)?([^/]*)$%); +$tp='' if(!defined($tp)); +$td=$tp; +} +# ... add children to parent maps (now that they exist): +$td=$dir; +while($td ne ) +{ +my($tp)=($td=~m%^(?:(.*)/)?([^/]*)$%); +$tp='' if(!defined($tp)); + +if(defined($revMap{$tp}{$td})) +{ +if($revMap{$tp}{$td} ne 'D') +{ +die Weird file
[PATCH 02/20] cvsserver: removed unused sha1Or-k mode from kopts_from_path
sha1Or-k was a vestige from an early, never-released attempt to handle some oddball cases of CRLF conversion (-k option). Ultimately it wasn't needed, and I should have gotten rid of it before submitting the CRLF patch in the first place. See also 90948a42892779 (add ability to guess -kb from contents). Signed-off-by: Matthew Ogilvie mmogilvi_...@miniinfo.net --- git-cvsserver.perl | 38 +- 1 file changed, 5 insertions(+), 33 deletions(-) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index b8eddab..f43d287 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -2444,42 +2444,14 @@ sub kopts_from_path } elsif( ($cfg-{gitcvs}{allbinary} =~ /^\s*guess\s*$/i) ) { -if( $srcType eq sha1Or-k -!defined($name) ) +if( is_binary($srcType,$name) ) { -my ($ret)=$state-{entries}{$path}{options}; -if( !defined($ret) ) -{ -$ret=$state-{opt}{k}; -if(defined($ret)) -{ -$ret=-k$ret; -} -else -{ -$ret=; -} -} -if( ! ($ret=~/^(|-kb|-kkv|-kkvl|-kk|-ko|-kv)$/) ) -{ -print E Bad -k option\n; -$log-warn(Bad -k option: $ret); -die Error: Bad -k option: $ret\n; -} - -return $ret; +$log-debug(... as binary); +return -kb; } else { -if( is_binary($srcType,$name) ) -{ -$log-debug(... as binary); -return -kb; -} -else -{ -$log-debug(... as text); -} +$log-debug(... as text); } } } @@ -2586,7 +2558,7 @@ sub open_blob_or_die die Unable to open file $name: $!\n; } } -elsif( $srcType eq sha1 || $srcType eq sha1Or-k ) +elsif( $srcType eq sha1 ) { unless ( defined ( $name ) and $name =~ /^[a-zA-Z0-9]{40}$/ ) { -- 1.7.10.2.484.gcd07cc5 -- To unsubscribe from this list: send the line unsubscribe git in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 07/20] cvsserver: split up long lines in req_{status,diff,log}
Signed-off-by: Matthew Ogilvie mmogilvi_...@miniinfo.net --- git-cvsserver.perl | 220 ++--- 1 file changed, 159 insertions(+), 61 deletions(-) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 9e71f30..eb8f138 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -1559,10 +1559,12 @@ sub req_status #$log-debug(status state : . Dumper($state)); # Grab a handle to the SQLite db and do any necessary updates -my $updater = GITCVS::updater-new($state-{CVSROOT}, $state-{module}, $log); +my $updater; +$updater = GITCVS::updater-new($state-{CVSROOT}, $state-{module}, $log); $updater-update(); -# if no files were specified, we need to work out what files we should be providing status on ... +# if no files were specified, we need to work out what files we should +# be providing status on ... argsfromdir($updater); # foreach file specified on the command line ... @@ -1570,14 +1572,19 @@ sub req_status { $filename = filecleanup($filename); -next if exists($state-{opt}{l}) index($filename, '/', length($state-{prependdir})) = 0; +if ( exists($state-{opt}{l}) + index($filename, '/', length($state-{prependdir})) = 0 ) +{ + next; +} my $meta = $updater-getmeta($filename); my $oldmeta = $meta; my $wrev = revparse($filename); -# If the working copy is an old revision, lets get that version too for comparison. +# If the working copy is an old revision, lets get that +# version too for comparison. if ( defined($wrev) and $wrev != $meta-{revision} ) { $oldmeta = $updater-getmeta($filename, $wrev); @@ -1585,49 +1592,95 @@ sub req_status # TODO : All possible statuses aren't yet implemented my $status; -# Files are up to date if the working copy and repo copy have the same revision, and the working copy is unmodified -$status = Up-to-date if ( defined ( $wrev ) and defined($meta-{revision}) and $wrev == $meta-{revision} -and -( ( $state-{entries}{$filename}{unchanged} and ( not defined ( $state-{entries}{$filename}{conflict} ) or $state-{entries}{$filename}{conflict} !~ /^\+=/ ) ) - or ( defined($state-{entries}{$filename}{modified_hash}) and $state-{entries}{$filename}{modified_hash} eq $meta-{filehash} ) ) - ); - -# Need checkout if the working copy has an older revision than the repo copy, and the working copy is unmodified -$status ||= Needs Checkout if ( defined ( $wrev ) and defined ( $meta-{revision} ) and $meta-{revision} $wrev - and - ( $state-{entries}{$filename}{unchanged} -or ( defined($state-{entries}{$filename}{modified_hash}) and $state-{entries}{$filename}{modified_hash} eq $oldmeta-{filehash} ) ) -); - -# Need checkout if it exists in the repo but doesn't have a working copy -$status ||= Needs Checkout if ( not defined ( $wrev ) and defined ( $meta-{revision} ) ); - -# Locally modified if working copy and repo copy have the same revision but there are local changes -$status ||= Locally Modified if ( defined ( $wrev ) and defined($meta-{revision}) and $wrev == $meta-{revision} and $state-{entries}{$filename}{modified_filename} ); - -# Needs Merge if working copy revision is less than repo copy and there are local changes -$status ||= Needs Merge if ( defined ( $wrev ) and defined ( $meta-{revision} ) and $meta-{revision} $wrev and $state-{entries}{$filename}{modified_filename} ); - -$status ||= Locally Added if ( defined ( $state-{entries}{$filename}{revision} ) and not defined ( $meta-{revision} ) ); -$status ||= Locally Removed if ( defined ( $wrev ) and defined ( $meta-{revision} ) and -$wrev == $meta-{revision} ); -$status ||= Unresolved Conflict if ( defined ( $state-{entries}{$filename}{conflict} ) and $state-{entries}{$filename}{conflict} =~ /^\+=/ ); -$status ||= File had conflicts on merge if ( 0 ); +# Files are up to date if the working copy and repo copy have +# the same revision, and the working copy is unmodified +if ( defined ( $wrev ) and defined($meta-{revision}) and + $wrev == $meta-{revision} and + ( ( $state-{entries}{$filename}{unchanged} and + ( not defined ( $state-{entries}{$filename}{conflict} ) or + $state-{entries}{$filename}{conflict} !~ /^\+=/ ) ) or + ( defined($state-{entries}{$filename}{modified_hash}) and + $state-{entries
[PATCH 05/20] cvsserver: remove unused functions _headrev and gethistory
Remove: - _headrev() - It uses similar functionality from getmeta() and gethead(). - gethistory() - It uses similar functions gethistorydense() and getlog(). Signed-off-by: Matthew Ogilvie mmogilvi_...@miniinfo.net --- git-cvsserver.perl | 36 1 file changed, 4 insertions(+), 32 deletions(-) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index cc39b6b..8b2accb 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -3440,19 +3440,6 @@ sub insert_head $insert_head-execute($name, $revision, $filehash, $commithash, $modified, $author, $mode); } -sub _headrev -{ -my $self = shift; -my $filename = shift; -my $tablename = $self-tablename(head); - -my $db_query = $self-{dbh}-prepare_cached(SELECT filehash, revision, mode FROM $tablename WHERE name=?,{},1); -$db_query-execute($filename); -my ( $hash, $revision, $mode ) = $db_query-fetchrow_array; - -return ( $hash, $revision, $mode ); -} - sub _get_prop { my $self = shift; @@ -3512,6 +3499,8 @@ sub gethead =head2 getlog +See also gethistorydense(). + =cut sub getlog @@ -3597,25 +3586,6 @@ sub commitmessage return $message; } -=head2 gethistory - -This function takes a filename (with path) argument and returns an arrayofarrays -containing revision,filehash,commithash ordered by revision descending - -=cut -sub gethistory -{ -my $self = shift; -my $filename = shift; -my $tablename = $self-tablename(revision); - -my $db_query; -$db_query = $self-{dbh}-prepare_cached(SELECT revision, filehash, commithash FROM $tablename WHERE name=? ORDER BY revision DESC,{},1); -$db_query-execute($filename); - -return $db_query-fetchall_arrayref; -} - =head2 gethistorydense This function takes a filename (with path) argument and returns an arrayofarrays @@ -3625,6 +3595,8 @@ This version of gethistory skips deleted entries -- so it is useful for annotate The 'dense' part is a reference to a '--dense' option available for git-rev-list and other git tools that depend on it. +See also getlog(). + =cut sub gethistorydense { -- 1.7.10.2.484.gcd07cc5 -- To unsubscribe from this list: send the line unsubscribe git in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 13/20] cvsserver: define a tag name character escape mechanism
CVS tags are officially only allowed to use [-_0-9A-Za-f]. Git refs commonly uses other characters, especially [./]. Such characters need to be escaped from CVS in order to be referenced. This just defines functions to escape/unescape names. The functions are not used yet. Signed-off-by: Matthew Ogilvie mmogilvi_...@miniinfo.net --- git-cvsserver.perl | 91 ++ 1 file changed, 91 insertions(+) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 1d929df..4eecc0b 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -3807,6 +3807,97 @@ sub gethistorydense return $result; } +=head2 escapeRefName + +Apply an escape mechanism to compensate for characters that +git ref names can have that CVS tags can not. + +=cut +sub escapeRefName +{ +my($self,$refName)=@_; + +# CVS officially only allows [-_A-Za-z0-9] in tag names (or in +# many contexts it can also be a CVS revision number). +# +# Git tags commonly use '/' and '.' as well, but also handle +# anything else just in case: +# +# = _-s- For '/'. +# = _-p- For '.'. +# = _-u- For underscore, in case someone wants a literal _- in +# a tag name. +# = _-xx- Where xx is the hexadecimal representation of the +# desired ASCII character byte. (for anything else) + +if(! $refName=~/^[1-9][0-9]*(\.[1-9][0-9]*)*$/) +{ +$refName=~s/_-/_-u--/g; +$refName=~s/\./_-p-/g; +$refName=~s%/%_-s-%g; +$refName=~s/[^-_a-zA-Z0-9]/sprintf(_-%02x-,$1)/eg; +} +} + +=head2 unescapeRefName + +Undo an escape mechanism to compensate for characters that +git ref names can have that CVS tags can not. + +=cut +sub unescapeRefName +{ +my($self,$refName)=@_; + +# see escapeRefName() for description of escape mechanism. + +$refName=~s/_-([spu]|[0-9a-f][0-9a-f])-/unescapeRefNameChar($1)/eg; + +# allowed tag names +# TODO: Perhaps use git check-ref-format, with an in-process cache of +# validated names? +if( !( $refName=~m%^[^-][-a-zA-Z0-9_/.]*$% ) || +( $refName=~m%[/.]$% ) || +( $refName=~/\.lock$/ ) || +( $refName=~m%\.\.|/\.|[[\\:?*~]|\@\{% ) ) # matching } +{ +# Error: +$log-warn(illegal refName: $refName); +$refName=undef; +} +return $refName; +} + +sub unescapeRefNameChar +{ +my($char)=@_; + +if($char eq s) +{ +$char=/; +} +elsif($char eq p) +{ +$char=.; +} +elsif($char eq u) +{ +$char=_; +} +elsif($char=~/^[0-9a-f][0-9a-f]$/) +{ +$char=chr(hex($char)); +} +else +{ +# Error case: Maybe it has come straight from user, and +# wasn't supposed to be escaped? Restore it the way we got it: +$char=_-$char-; +} + +return $char; +} + =head2 in_array() from Array::PAT - mimics the in_array() function -- 1.7.10.2.484.gcd07cc5 -- To unsubscribe from this list: send the line unsubscribe git in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 19/20] cvsserver: add t9402 to test branch and tag refs
Signed-off-by: Matthew Ogilvie mmogilvi_...@miniinfo.net --- t/t9402-git-cvsserver-refs.sh | 558 ++ 1 file changed, 558 insertions(+) create mode 100755 t/t9402-git-cvsserver-refs.sh diff --git a/t/t9402-git-cvsserver-refs.sh b/t/t9402-git-cvsserver-refs.sh new file mode 100755 index 000..a256faa --- /dev/null +++ b/t/t9402-git-cvsserver-refs.sh @@ -0,0 +1,558 @@ +#!/bin/sh + +test_description='git-cvsserver and git refspecs + +tests ability for git-cvsserver to switch between and compare +tags, branches and other git refspecs' + +. ./test-lib.sh + +# + +check_start_tree() { +rm -f $WORKDIR/check.list +echo start $1 ${WORKDIR}/check.log +} + +check_file() { +sandbox=$1 +file=$2 +ver=$3 +GIT_DIR=$SERVERDIR git show ${ver}:${file} \ + $WORKDIR/check.got 2 $WORKDIR/check.stderr +test_cmp $WORKDIR/check.got $sandbox/$file +stat=$? +echo check_file $sandbox $file $ver : $stat $WORKDIR/check.log +echo $file $WORKDIR/check.list +return $stat +} + +check_end_tree() { +sandbox=$1 +expectCount=$(wc -l $WORKDIR/check.list) +cvsCount=$(find $sandbox -name CVS -prune -o -type f -print | wc -l) +test x$cvsCount = x$expectCount +stat=$? +echo check_end $sandbox : $stat cvs=$cvsCount expect=$expectCount \ + $WORKDIR/check.log +return $stat +} + +check_end_full_tree() { +sandbox=$1 +ver=$2 +expectCount=$(wc -l $WORKDIR/check.list) +cvsCount=$(find $sandbox -name CVS -prune -o -type f -print | wc -l) +gitCount=$(git ls-tree -r $2 | wc -l) +test x$cvsCount = x$expectCount -a x$gitCount = x$expectCount +stat=$? +echo check_end $sandbox : $stat cvs=$cvsCount git=$gitCount expect=$expectCount \ + $WORKDIR/check.log +return $stat +} + +# + +check_diff() { +diffFile=$1 +vOld=$2 +vNew=$3 +rm -rf diffSandbox +git clone -q -n . diffSandbox +( cd diffSandbox + git checkout $vOld + git apply -p0 --index ../$diffFile + git diff --exit-code $vNew ) check_diff_apply.out 21 +} + +# + +cvs /dev/null 21 +if test $? -ne 1 +then +skip_all='skipping git-cvsserver tests, cvs not found' +test_done +fi +if ! test_have_prereq PERL +then +skip_all='skipping git-cvsserver tests, perl not available' +test_done +fi +$PERL_PATH -e 'use DBI; use DBD::SQLite' /dev/null 21 || { +skip_all='skipping git-cvsserver tests, Perl SQLite interface unavailable' +test_done +} + +unset GIT_DIR GIT_CONFIG +WORKDIR=$(pwd) +SERVERDIR=$(pwd)/gitcvs.git +git_config=$SERVERDIR/config +CVSROOT=:fork:$SERVERDIR +CVSWORK=$(pwd)/cvswork +CVS_SERVER=git-cvsserver +export CVSROOT CVS_SERVER + +rm -rf $CVSWORK $SERVERDIR +test_expect_success 'setup v1, b1' ' +echo Simple text file textfile.c +echo t2 t2 +mkdir adir +echo adir/afile line1 adir/afile +echo adir/afile line2 adir/afile +echo adir/afile line3 adir/afile +echo adir/afile line4 adir/afile +echo adir/a2file adir/a2file +mkdir adir/bdir +echo adir/bdir/bfile line 1 adir/bdir/bfile +echo adir/bdir/bfile line 2 adir/bdir/bfile +echo adir/bdir/b2file adir/bdir/b2file +git add textfile.c t2 adir +git commit -q -m First Commit (v1) +git tag v1 +git branch b1 +git clone -q --bare $WORKDIR/.git $SERVERDIR /dev/null 21 +GIT_DIR=$SERVERDIR git config --bool gitcvs.enabled true +GIT_DIR=$SERVERDIR git config gitcvs.logfile $SERVERDIR/gitcvs.log +' + +rm -rf cvswork +test_expect_success 'cvs co v1' ' +cvs -f -Q co -r v1 -d cvswork master cvs.log 21 +check_start_tree cvswork +check_file cvswork textfile.c v1 +check_file cvswork t2 v1 +check_file cvswork adir/afile v1 +check_file cvswork adir/a2file v1 +check_file cvswork adir/bdir/bfile v1 +check_file cvswork adir/bdir/b2file v1 +check_end_tree cvswork +' + +rm -rf cvswork +test_expect_success 'cvs co b1' ' +cvs -f co -r b1 -d cvswork master cvs.log 21 +check_start_tree cvswork +check_file cvswork textfile.c v1 +check_file cvswork t2 v1 +check_file cvswork adir/afile v1 +check_file cvswork adir/a2file v1 +check_file cvswork adir/bdir/bfile v1 +check_file cvswork adir/bdir/b2file v1 +check_end_tree cvswork +' + +test_expect_success 'cvs co b1 [cvswork3]' ' +cvs -f co -r b1 -d cvswork3 master cvs.log 21 +check_start_tree cvswork3 +check_file cvswork3 textfile.c v1 +check_file cvswork3 t2 v1 +check_file cvswork3 adir/afile v1 +check_file cvswork3 adir/a2file v1 +check_file cvswork3 adir/bdir/bfile v1 +check_file cvswork3 adir/bdir/b2file v1 +check_end_full_tree cvswork3 v1 +' + +test_expect_success 'edit cvswork3 and save diff' ' +( cd cvswork3 + sed -i -e s/line1/line1 - data/ adir/afile + echo afile5 adir/afile5 + rm t2 + cvs -f add adir/afile5
[PATCH 17/20] cvsserver: Add version awareness to argsfromdir
Signed-off-by: Matthew Ogilvie mmogilvi_...@miniinfo.net --- git-cvsserver.perl | 228 ++--- 1 file changed, 198 insertions(+), 30 deletions(-) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 7bb6f83..5e558d1 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -2226,55 +2226,222 @@ sub argsplit } } -# This method uses $state-{directory} to populate $state-{args} with a list of filenames -sub argsfromdir +# Used by argsfromdir +sub expandArg { -my $updater = shift; +my ($updater,$outNameMap,$outDirMap,$path,$isDir) = @_; -$state-{args} = [] if ( scalar(@{$state-{args}}) == 1 and $state-{args}[0] eq . ); +my $fullPath = filecleanup($path); -return if ( scalar ( @{$state-{args}} ) 1 ); + # Is it a directory? +if( defined($state-{dirMap}{$fullPath}) || +defined($state-{dirMap}{$fullPath/}) ) +{ + # It is a directory in the user's sandbox. +$isDir=1; -my @gethead = @{$updater-gethead}; +if(defined($state-{entries}{$fullPath})) +{ +$log-fatal(Inconsistent file/dir type); +die Inconsistent file/dir type; +} +} +elsif(defined($state-{entries}{$fullPath})) +{ + # It is a file in the user's sandbox. +$isDir=0; +} +my($revDirMap,$otherRevDirMap); +if(!defined($isDir) || $isDir) +{ + # Resolve version tree for sticky tag: + # (for now we only want list of files for the version, not + # particular versions of those files: assume it is a directory + # for the moment; ignore Entry's stick tag) + + # Order of precedence of sticky tags: + #-A [head] + #-r /tag/ + #[file entry sticky tag, but that is only relevant to files] + #[the tag specified in dir req_Sticky] + #[the tag specified in a parent dir req_Sticky] + #[head] + # Also, -r may appear twice (for diff). + # + # FUTURE: When/if -j (merges) are supported, we also + # need to add relevant files from one or two + # versions specified with -j. + +if(exists($state-{opt}{A})) +{ +$revDirMap=$updater-getRevisionDirMap(); +} +elsif( defined($state-{opt}{r}) and + ref $state-{opt}{r} eq ARRAY ) +{ +$revDirMap=$updater-getRevisionDirMap($state-{opt}{r}[0]); +$otherRevDirMap=$updater-getRevisionDirMap($state-{opt}{r}[1]); +} +elsif(defined($state-{opt}{r})) +{ +$revDirMap=$updater-getRevisionDirMap($state-{opt}{r}); +} +else +{ +my($sticky)=getDirStickyInfo($fullPath); +$revDirMap=$updater-getRevisionDirMap($sticky-{tag}); +} -# push added files -foreach my $file (keys %{$state-{entries}}) { - if ( exists $state-{entries}{$file}{revision} - $state-{entries}{$file}{revision} eq '0' ) - { - push @gethead, { name = $file, filehash = 'added' }; - } + # Is it a directory? +if( defined($revDirMap-{$fullPath}) || +defined($otherRevDirMap-{$fullPath}) ) +{ +$isDir=1; +} } -if ( scalar(@{$state-{args}}) == 1 ) + # What to do with it? +if(!$isDir) { -my $arg = $state-{args}[0]; -$arg .= $state-{prependdir} if ( defined ( $state-{prependdir} ) ); - -$log-info(Only one arg specified, checking for directory expansion on '$arg'); +$outNameMap-{$fullPath}=1; +} +else +{ +$outDirMap-{$fullPath}=1; -foreach my $file ( @gethead ) +if(defined($revDirMap-{$fullPath})) { -next if ( $file-{filehash} eq deleted and not defined ( $state-{entries}{$file-{name}} ) ); -next unless ( $file-{name} =~ /^$arg\// or $file-{name} eq $arg ); -push @{$state-{args}}, $file-{name}; +addDirMapFiles($updater,$outNameMap,$outDirMap, + $revDirMap-{$fullPath}); } +if( defined($otherRevDirMap) +defined($otherRevDirMap-{$fullPath}) ) +{ +addDirMapFiles($updater,$outNameMap,$outDirMap, + $otherRevDirMap-{$fullPath}); +} +} +} -shift @{$state-{args}} if ( scalar(@{$state-{args}}) 1 ); -} else { -$log-info(Only one arg specified, populating file list automatically); +# Used by argsfromdir +# Add entries from dirMap to outNameMap. Also recurse into entries +# that are subdirectories. +sub addDirMapFiles +{ +my($updater,$outNameMap,$outDirMap,$dirMap)=@_; -$state-{args} = []; +my($fullName); +foreach $fullName (keys(%$dirMap)) +{ +my $cleanName=$fullName; +if(defined($state-{prependdir
[PATCH 16/20] cvsserver: generalize getmeta() to recognize commit refs
This allows getmeta() to recognize any commitish (sha1, tag/branch name, etc). Signed-off-by: Matthew Ogilvie mmogilvi_...@miniinfo.net --- git-cvsserver.perl | 156 + 1 file changed, 145 insertions(+), 11 deletions(-) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 53d8de7..7bb6f83 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -4041,6 +4041,19 @@ sub getlog This function takes a filename (with path) argument and returns a hashref of metadata for that file. +There are several ways $revision can be specified: + + - A reference to hash that contains a tag that is the + actual revision (one of the below). TODO: Also allow it to + specify a date in the hash. + - undef, to refer to the latest version on the main branch. + - Full CVS client revision number (mapped to integer in DB, without the + 1. prefix), + - Complex CVS-compatible special revision number for + non-linear history (see comment below) + - git commit sha1 hash + - branch or tag name + =cut sub getmeta @@ -4051,23 +4064,144 @@ sub getmeta my $tablename_rev = $self-tablename(revision); my $tablename_head = $self-tablename(head); -my $db_query; -if ( defined($revision) and $revision =~ /^1\.(\d+)$/ ) +if ( ref($revision) eq HASH ) { -my ($intRev) = $1; -$db_query = $self-{dbh}-prepare_cached(SELECT * FROM $tablename_rev WHERE name=? AND revision=?,{},1); -$db_query-execute($filename, $intRev); +$revision = $revision-{tag}; +} + +# Overview of CVS revision numbers: +# +# General CVS numbering scheme: +# - Basic mainline branch numbers: 1.1, 1.2, 1.3, etc. +# - Result of cvs checkin -r (possible, but not really +# recommended): 2.1, 2.2, etc +# - Branch tag: 1.2.0.n, where 1.2 is revision it was branched +# from, 0 is a magic placeholder that identifies it as a +# branch tag instead of a version tag, and n is 2 times the +# branch number off of 1.2, starting with 2. +# - Version on a branch: 1.2.n.x, where 1.2 is branch-from, n +# is branch number off of 1.2 (like n above), and x is +# the version number on the branch. +# - Branches can branch off of branches: 1.3.2.7.4.1 (even number +# of components). +# - Odd ns are used by vendor branches that result +# from cvs import. Vendor branches have additional +# strangeness in the sense that the main rcs head of the main +# branch will (temporarily until first normal commit) point +# to the version on the vendor branch, rather than the actual +# main branch. (FUTURE: This may provide an opportunity +# to use strange revision numbers for fast-forward-merged +# branch tip when CVS client is asking for the main branch.) +# +# git-cvsserver CVS-compatible special numbering schemes: +# - Currently git-cvsserver only tries to be identical to CVS for +# simple 1.x numbers on the main branch (as identified +# by the module name that was originally cvs checkout'ed). +# - The database only stores the x part, for historical reasons. +# But most of the rest of the cvsserver preserves +# and thinks using the full revision number. +# - To handle non-linear history, it uses a version of the form +# 2.1.1.2000.b.b.b, where the 2.1.1.2000 is to help uniquely +# identify this as a special revision number, and there are +# 20 b's that together encode the sha1 git commit from which +# this version of this file originated. Each b is +# the numerical value of the corresponding byte plus +# 100. +# - plus 100 avoids 0s, and also reduces the +#likelyhood of a collision in the case that someone someday +#writes an import tool that tries to preserve original +#CVS revision numbers, and the original CVS data had done +#lots of branches off of branches and other strangeness to +#end up with a real version number that just happens to look +#like this special revision number form. Also, if needed +#there are several ways to extend/identify alternative encodings +#within the 2.1.1.2000 part if necessary. +# - Unlike real CVS revisions, you can't really reconstruct what +#relation a revision of this form has to other revisions. +# - FUTURE: TODO: Rework database somehow to make up and remember +# fully-CVS-compatible branches and branch version numbers. + +my $meta; +if ( defined($revision) ) +{ +if ( $revision =~ /^1\.(\d+)$/ ) +{ +my ($intRev) = $1; +my $db_query; +$db_query = $self-{dbh}-prepare_cached( +SELECT * FROM $tablename_rev WHERE name=? AND revision
[PATCH 20/20] cvsserver Documentation: new cvs ... -r support
Signed-off-by: Matthew Ogilvie mmogilvi_...@miniinfo.net --- Documentation/git-cvsserver.txt | 37 + 1 file changed, 37 insertions(+) diff --git a/Documentation/git-cvsserver.txt b/Documentation/git-cvsserver.txt index 88d814a..940c2ba 100644 --- a/Documentation/git-cvsserver.txt +++ b/Documentation/git-cvsserver.txt @@ -359,6 +359,43 @@ Operations supported All the operations required for normal use are supported, including checkout, diff, status, update, log, add, remove, commit. + +Most CVS command arguments that read CVS tags or revision numbers +(typically -r) work, and also support any git refspec +(tag, branch, commit ID, etc). +However, CVS revision numbers for non-default branches are not well +emulated, and cvs log does not show tags or branches at +all. (Non-main-branch CVS revision numbers superficially resemble CVS +revision numbers, but they actually encode a git commit ID directly, +rather than represent the number of revisions since the branch point.) + +Note that there are two ways to checkout a particular branch. +As described elsewhere on this page, the module parameter +of cvs checkout is interpreted as a branch name, and it becomes +the main branch. It remains the main branch for a given sandbox +even if you temporarily make another branch sticky with +cvs update -r. Alternatively, the -r argument can indicate +some other branch to actually checkout, even though the module +is still the main branch. Tradeoffs (as currently +implemented): Each new module creates a new database on disk with +a history for the given module, and after the database is created, +operations against that main branch are fast. Or alternatively, +-r doesn't take any extra disk space, but may be significantly slower for +many operations, like cvs update. + +If you want to refer to a git refspec that has characters that are +not allowed by CVS, you have two options. First, it may just work +to supply the git refspec directly to the appropriate CVS -r argument; +some CVS clients don't seem to do much sanity checking of the argument. +Second, if that fails, you can use a special character escape mechanism +that only uses characters that are valid in CVS tags. A sequence +of 4 or 5 characters of the form (underscore (`_`), dash (`-`), +one or two characters, and dash (`-`)) can encode various characters based +on the one or two letters: `s` for slash (`/`), `p` for +period (`.`), `u` for underscore (`_`), or two hexadecimal digits +for any byte value at all (typically an ASCII number, or perhaps a part +of a UTF-8 encoded character). + Legacy monitoring operations are not supported (edit, watch and related). Exports and tagging (tags and branches) are not supported at this stage. -- 1.7.10.2.484.gcd07cc5 -- To unsubscribe from this list: send the line unsubscribe git in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 10/20] cvsserver status: provide real sticky info
Signed-off-by: Matthew Ogilvie mmogilvi_...@miniinfo.net --- git-cvsserver.perl| 24 t/t9401-git-cvsserver-crlf.sh | 35 +++ 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 4d514b4..c5ebfa0 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -502,7 +502,7 @@ sub req_Entry #$log-debug(req_Entry : $data); -my @data = split(/\//, $data); +my @data = split(/\//, $data, -1); $state-{entries}{$state-{directory}.$data[1]} = { revision= $data[2], @@ -1681,9 +1681,25 @@ sub req_status print M Repository revision:\t . $meta-{revision} . \t$state-{CVSROOT}/$state-{module}/$filename,v\n; -print M Sticky Tag:\t\t(none)\n; -print M Sticky Date:\t\t(none)\n; -print M Sticky Options:\t\t(none)\n; +my($tagOrDate)=$state-{entries}{$filename}{tag_or_date}; +my($tag)=($tagOrDate=~m/^T(.+)$/); +if( !defined($tag) ) +{ +$tag=(none); +} +print M Sticky Tag:\t\t$tag\n; +my($date)=($tagOrDate=~m/^D(.+)$/); +if( !defined($date) ) +{ +$date=(none); +} +print M Sticky Date:\t\t$date\n; +my($options)=$state-{entries}{$filename}{options}; +if( $options eq ) +{ +$options=(none); +} +print M Sticky Options:\t\t$options\n; } else { print M Repository revision:\tNo revision control file\n; } diff --git a/t/t9401-git-cvsserver-crlf.sh b/t/t9401-git-cvsserver-crlf.sh index ff6d6fb..7c3c39e 100755 --- a/t/t9401-git-cvsserver-crlf.sh +++ b/t/t9401-git-cvsserver-crlf.sh @@ -38,6 +38,25 @@ not_present() { fi } +check_status_options() { +(cd $1 +GIT_CONFIG=$git_config cvs -Q status $2 ${WORKDIR}/status.out 21 +) +if [ x$? != x0 ] ; then +echo Error from cvs status: $1 $2 ${WORKDIR}/marked.log +return 1; +fi +got=$(sed -n -e 's/^\s*Sticky Options:\s*//p' ${WORKDIR}/status.out) +expect=$3 +if [ x$expect = x ] ; then +expect=(none) +fi +test x$got = x$expect +stat=$? +echo cvs status: $1 $2 $stat '$3' '$got' ${WORKDIR}/marked.log +return $stat +} + cvs /dev/null 21 if test $? -ne 1 then @@ -233,6 +252,22 @@ test_expect_success 'cvs co another copy (guess)' ' marked_as cvswork2/subdir newfile.c ' +test_expect_success 'cvs status - sticky options' ' +check_status_options cvswork2 textfile.c +check_status_options cvswork2 binfile.bin -kb +check_status_options cvswork2 .gitattributes +check_status_options cvswork2 mixedUp.c -kb +check_status_options cvswork2 multiline.c -kb +check_status_options cvswork2 multilineTxt.c +check_status_options cvswork2/subdir withCr.bin -kb +check_status_options cvswork2 subdir/withCr.bin -kb +check_status_options cvswork2/subdir file.h +check_status_options cvswork2 subdir/file.h +check_status_options cvswork2/subdir unspecified.other +check_status_options cvswork2/subdir newfile.bin +check_status_options cvswork2/subdir newfile.c +' + test_expect_success 'add text (guess)' ' (cd cvswork echo simpleText simpleText.c -- 1.7.10.2.484.gcd07cc5 -- To unsubscribe from this list: send the line unsubscribe git in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 15/20] cvsserver: implement req_Sticky and related utilities
Nothing sets sticky yet, or uses the values set by this, but soon... Signed-off-by: Matthew Ogilvie mmogilvi_...@miniinfo.net --- git-cvsserver.perl | 171 - 1 file changed, 169 insertions(+), 2 deletions(-) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 16e2e1f..53d8de7 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -60,6 +60,7 @@ my $methods = { 'Valid-responses' = \req_Validresponses, 'valid-requests' = \req_validrequests, 'Directory' = \req_Directory, +'Sticky' = \req_Sticky, 'Entry' = \req_Entry, 'Modified'= \req_Modified, 'Unchanged' = \req_Unchanged, @@ -470,11 +471,19 @@ sub req_Directory { $log-info(Setting prepend to '$state-{path}'); $state-{prependdir} = $state-{path}; +my %entries; foreach my $entry ( keys %{$state-{entries}} ) { -$state-{entries}{$state-{prependdir} . $entry} = $state-{entries}{$entry}; -delete $state-{entries}{$entry}; +$entries{$state-{prependdir} . $entry} = $state-{entries}{$entry}; } +$state-{entries}=\%entries; + +my %dirMap; +foreach my $dir ( keys %{$state-{dirMap}} ) +{ +$dirMap{$state-{prependdir} . $dir} = $state-{dirMap}{$dir}; +} +$state-{dirMap}=\%dirMap; } if ( defined ( $state-{prependdir} ) ) @@ -482,9 +491,60 @@ sub req_Directory $log-debug(Prepending '$state-{prependdir}' to state|directory); $state-{directory} = $state-{prependdir} . $state-{directory} } + +if ( ! defined($state-{dirMap}{$state-{directory}}) ) +{ +$state-{dirMap}{$state-{directory}} = +{ +'names' = {} +#'tagspec' = undef +}; +} + $log-debug(req_Directory : localdir=$data repository=$repository path=$state-{path} directory=$state-{directory} module=$state-{module}); } +# Sticky tagspec \n +# Response expected: no. Tell the server that the directory most +# recently specified with Directory has a sticky tag or date +# tagspec. The first character of tagspec is T for a tag, D for +# a date, or some other character supplied by a Set-sticky +# response from a previous request to the server. The remainder +# of tagspec contains the actual tag or date, again as supplied +# by Set-sticky. +# The server should remember Static-directory and Sticky requests +# for a particular directory; the client need not resend them each +# time it sends a Directory request for a given directory. However, +# the server is not obliged to remember them beyond the context +# of a single command. +sub req_Sticky +{ +my ( $cmd, $tagspec ) = @_; + +my ( $stickyInfo ); +if($tagspec eq ) +{ +# nothing +} +elsif($tagspec=~/^T([^ ]+)\s*$/) +{ +$stickyInfo = { 'tag' = $1 }; +} +elsif($tagspec=~/^D([0-9.]+)\s*$/) +{ +$stickyInfo= { 'date' = $1 }; +} +else +{ +die Unknown tag_or_date format\n; +} +$state-{dirMap}{$state-{directory}}{stickyInfo}=$stickyInfo; + +$log-debug(req_Sticky : tagspec=$tagspec repository=$state-{repository} +. path=$state-{path} directory=$state-{directory} +. module=$state-{module}); +} + # Entry entry-line \n # Response expected: no. Tell the server what version of a file is on the # local machine. The name in entry-line is a name relative to the directory @@ -511,6 +571,8 @@ sub req_Entry tag_or_date = $data[5], }; +$state-{dirMap}{$state-{directory}}{names}{$data[1]} = 'F'; + $log-info(Received entry line '$data' = ' . $state-{directory} . $data[1] . '); } @@ -2213,6 +2275,110 @@ sub argsfromdir } } + +## look up directory sticky tag, of either fullPath or a parent: +sub getDirStickyInfo +{ +my($fullPath)=@_; + +$fullPath=~s%/+$%%; +while($fullPath ne !defined($state-{dirMap}{$fullPath/})) +{ +$fullPath=~s%/?[^/]*$%%; +} + +if( !defined($state-{dirMap}{$fullPath/}) +( $fullPath eq || + $fullPath eq . ) ) +{ +return $state-{dirMap}{}{stickyInfo}; +} +else +{ +return $state-{dirMap}{$fullPath/}{stickyInfo}; +} +} + +# Resolve precedence of various ways of specifying which version of +# a file you want. Returns undef (for default head), or a ref to a hash +# that contains tag and/or date keys. +sub resolveStickyInfo +{ +my($filename,$stickyTag,$stickyDate,$reset) = @_; + +# Order of precedence of sticky tags: +#-A [head] +#-r /tag/ +#[file entry sticky tag] +#[the tag specified in dir req_Sticky] +#[the tag specified in a parent dir req_Sticky] +#[head] + +my $result; +if($reset
[PATCH 11/20] cvsserver: factor out git-log parsing logic
Some field conversion was already duplicated, and more calls will be added soon. Signed-off-by: Matthew Ogilvie mmogilvi_...@miniinfo.net --- git-cvsserver.perl | 176 - 1 file changed, 105 insertions(+), 71 deletions(-) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index c5ebfa0..dca0ed6 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -3157,45 +3157,10 @@ sub update push @git_log_params, $self-{module}; } # git-rev-list is the backend / plumbing version of git-log -open(GITLOG, '-|', 'git', 'rev-list', @git_log_params) or die Cannot call git-rev-list: $!; - -my @commits; - -my %commit = (); - -while ( GITLOG ) -{ -chomp; -if (m/^commit\s+(.*)$/) { -# on ^commit lines put the just seen commit in the stack -# and prime things for the next one -if (keys %commit) { -my %copy = %commit; -unshift @commits, \%copy; -%commit = (); -} -my @parents = split(m/\s+/, $1); -$commit{hash} = shift @parents; -$commit{parents} = \@parents; -} elsif (m/^(\w+?):\s+(.*)$/ !exists($commit{message})) { -# on rfc822-like lines seen before we see any message, -# lowercase the entry and put it in the hash as key-value -$commit{lc($1)} = $2; -} else { -# message lines - skip initial empty line -# and trim whitespace -if (!exists($commit{message}) m/^\s*$/) { -# define it to mark the end of headers -$commit{message} = ''; -next; -} -s/^\s+//; s/\s+$//; # trim ws -$commit{message} .= $_ . \n; -} -} -close GITLOG; - -unshift @commits, \%commit if ( keys %commit ); +open(my $gitLogPipe, '-|', 'git', 'rev-list', @git_log_params) +or die Cannot call git-rev-list: $!; +my @commits=readCommits($gitLogPipe); +close $gitLogPipe; # Now all the commits are in the @commits bucket # ordered by time DESC. for each commit that needs processing, @@ -3294,7 +3259,7 @@ sub update } # convert the date to CVS-happy format -$commit-{date} = $2 $1 $4 $3 $5 if ( $commit-{date} =~ /^\w+\s+(\w+)\s+(\d+)\s+(\d+:\d+:\d+)\s+(\d+)\s+([+-]\d+)$/ ); +my $cvsDate = convertToCvsDate($commit-{date}); if ( defined ( $lastpicked ) ) { @@ -3303,7 +3268,7 @@ sub update while ( FILELIST ) { chomp; -unless ( /^:\d{6}\s+\d{3}(\d)\d{2}\s+[a-zA-Z0-9]{40}\s+([a-zA-Z0-9]{40})\s+(\w)$/o ) +unless ( /^:\d{6}\s+([0-7]{6})\s+[a-f0-9]{40}\s+([a-f0-9]{40})\s+(\w)$/o ) { die(Couldn't process git-diff-tree line : $_); } @@ -3313,11 +3278,7 @@ sub update # $log-debug(File mode=$mode, hash=$hash, change=$change, name=$name); -my $git_perms = ; -$git_perms .= r if ( $mode 4 ); -$git_perms .= w if ( $mode 2 ); -$git_perms .= x if ( $mode 1 ); -$git_perms = rw if ( $git_perms eq ); +my $dbMode = convertToDbMode($mode); if ( $change eq D ) { @@ -3327,11 +3288,11 @@ sub update revision = $head-{$name}{revision} + 1, filehash = deleted, commithash = $commit-{hash}, -modified = $commit-{date}, +modified = $cvsDate, author = $commit-{author}, -mode = $git_perms, +mode = $dbMode, }; -$self-insert_rev($name, $head-{$name}{revision}, $hash, $commit-{hash}, $commit-{date}, $commit-{author}, $git_perms); +$self-insert_rev($name, $head-{$name}{revision}, $hash, $commit-{hash}, $cvsDate, $commit-{author}, $dbMode); } elsif ( $change eq M || $change eq T ) { @@ -3341,11 +3302,11 @@ sub update revision = $head-{$name}{revision} + 1, filehash = $hash, commithash = $commit-{hash}, -modified = $commit-{date}, +modified = $cvsDate, author = $commit-{author}, -mode = $git_perms, +mode = $dbMode, }; -$self-insert_rev($name, $head-{$name}{revision}, $hash, $commit-{hash}, $commit-{date}, $commit-{author}, $git_perms); +$self-insert_rev($name, $head-{$name}{revision}, $hash, $commit-{hash}, $cvsDate, $commit-{author}, $dbMode
[PATCH 12/20] cvsserver: cleanup extra slashes in filename arguments
Signed-off-by: Matthew Ogilvie mmogilvi_...@miniinfo.net --- git-cvsserver.perl | 28 1 file changed, 28 insertions(+) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index dca0ed6..1d929df 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -2309,6 +2309,9 @@ sub filenamesplit return ( $filepart, $dirpart ); } +# Cleanup various junk in filename (try to canonicalize it), and +# add prependdir to accomodate running CVS client from a +# subdirectory (so the output is relative to top directory of the project). sub filecleanup { my $filename = shift; @@ -2320,11 +2323,36 @@ sub filecleanup return undef; } +if($filename eq .) +{ +$filename=; +} $filename =~ s/^\.\///g; +$filename =~ s%/+%/%g; $filename = $state-{prependdir} . $filename; +$filename =~ s%/$%%; return $filename; } +# Remove prependdir from the path, so that is is relative to the directory +# the CVS client was started from, rather than the top of the project. +# Essentially the inverse of filecleanup(). +sub remove_prependdir +{ +my($path) = @_; +if(defined($state-{prependdir}) $state-{prependdir} ne ) +{ +my($pre)=$state-{prependdir}; +$pre=~s%/$%%; +if(!($path=~s%^\Q$pre\E/?%%)) +{ +$log-fatal(internal error missing prependdir); +die(internal error missing prependdir); +} +} +return $path; +} + sub validateGitDir { if( !defined($state-{CVSROOT}) ) -- 1.7.10.2.484.gcd07cc5 -- To unsubscribe from this list: send the line unsubscribe git in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html