Re: [PATCH] subtree/Makefile: Standardize (esp. for packagers)

2014-04-29 Thread Matthew Ogilvie
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

2014-01-23 Thread Matthew Ogilvie
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

2014-01-22 Thread Matthew Ogilvie
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

2013-12-07 Thread Matthew Ogilvie
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

2013-12-07 Thread Matthew Ogilvie
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

2013-12-07 Thread Matthew Ogilvie
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

2013-12-07 Thread Matthew Ogilvie
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

2013-11-28 Thread Matthew Ogilvie
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

2012-10-14 Thread Matthew Ogilvie
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

2012-10-14 Thread Matthew Ogilvie
  - 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

2012-10-13 Thread Matthew Ogilvie
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

2012-10-13 Thread Matthew Ogilvie
'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

2012-10-13 Thread Matthew Ogilvie
  - 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

2012-10-13 Thread Matthew Ogilvie
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

2012-10-13 Thread Matthew Ogilvie
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

2012-10-13 Thread Matthew Ogilvie
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

2012-10-13 Thread Matthew Ogilvie
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}

2012-10-13 Thread Matthew Ogilvie
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

2012-10-13 Thread Matthew Ogilvie
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

2012-10-13 Thread Matthew Ogilvie
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

2012-10-13 Thread Matthew Ogilvie
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

2012-10-13 Thread Matthew Ogilvie
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

2012-10-13 Thread Matthew Ogilvie
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

2012-10-13 Thread Matthew Ogilvie
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

2012-10-13 Thread Matthew Ogilvie
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

2012-10-13 Thread Matthew Ogilvie
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

2012-10-13 Thread Matthew Ogilvie
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

2012-10-13 Thread Matthew Ogilvie
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