Git development
 help / color / mirror / Atom feed
* Re: Volume of commits
From: Sven Verdoolaege @ 2007-07-13 10:30 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: VMiklos, Fredrik Tolf, git
In-Reply-To: <Pine.LNX.4.64.0707121451290.4516@racer.site>

On Thu, Jul 12, 2007 at 02:59:53PM +0100, Johannes Schindelin wrote:
> In this case, "messy history" means that there are tiny patches which are 
> often in the wrong order, or should be squashed into one commit.  "git 
> rebase -i upstream" presents you with the list of A - HEAD, and you can 
> reorder the patches.  If you want to, you can combine ("squash") some 
> into one commit, or you can skip it, by removing the corresponding line.

If I squash a whole series of commits, how do I prevent git-rebase -i
from firing up an editor after every single commit in the series?

Also, if I do the following:

bash-3.00$ git init
Initialized empty Git repository in .git/
bash-3.00$ for i in a b c; do touch $i; git add $i; git commit -m $i -a; done
Created initial commit 19a8485: a
 0 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 a
Created commit 4a00f85: b
 0 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 b
Created commit defe3b5: c
 0 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 c
bash-3.00$ git rebase -i  HEAD~2

and then replace the second "pick" by "squash", then I get presented
a commit message that contains the commit message of "c" twice and
after the rebase there are still three commits in the history.
This is with git version 1.5.3.rc1.10.gae1ae
(on top of v1.5.3-rc1-4-gaf83bed).

skimo

^ permalink raw reply

* Re: Better handling of local changes in 'gitk'?
From: Paul Mackerras @ 2007-07-13 10:09 UTC (permalink / raw)
  To: Linus Torvalds, Git Mailing List
In-Reply-To: <18071.19489.6733.665052@cargo.ozlabs.ibm.com>

I wrote:

> Try this, let me know what you think.  I called the changes in the
> working directory "Local uncommitted changes, not checked in to index"
> and the changes in the index "Local changes checked in to index but
> not committed".  If you prefer some other wording, let me know.  I
> made the working directory commit red (as before) and the index commit
> magenta, as being between red and blue.  The index commit gets a fake
> SHA1 id of 00..001.

I should add that I'm sure that gitk won't yet do the right thing if
there are files in the index at other than stage 0, or if the local
changes are the result of an incomplete merge.  (In the latter case I
suppose one or other of the fake commits should have multiple parents,
but I don't have the infrastructure to add fake commits with multiple
parents yet.)

Paul.

^ permalink raw reply

* git-svn patch faulty (was: failing to send patches to the list)
From: martin f krafft @ 2007-07-13  9:56 UTC (permalink / raw)
  To: git discussion list
In-Reply-To: <20070713093050.GA18001@lapse.madduck.net>

[-- Attachment #1: Type: text/plain, Size: 762 bytes --]

Hi there,

still not having figured out the problem with the list (and I did
contact postmaster@), I just discovered that in fact the patch only
handles git-svn clone, not git-svn init. Thus, it's best to ignore
it for now. I am sorry for not taking enough care before publishing
it.

The openssl-autodetect patch does work and is ready for inclusion,
if you agree.

-- 
martin;              (greetings from the heart of the sun.)
  \____ echo mailto: !#^."<*>"|tr "<*> mailto:" net@madduck
 
spamtraps: madduck.bogus@madduck.net
 
"if builders built buildings the way
 programmers wrote programs,
 then the first woodpecker that came along
 would destroy civilization."
                                                  -- gerald weinberg

[-- Attachment #2: Digital signature (GPG/PGP) --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply

* Re: Better handling of local changes in 'gitk'?
From: Paul Mackerras @ 2007-07-13  9:55 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List
In-Reply-To: <alpine.LFD.0.999.0707121214420.20061@woody.linux-foundation.org>

Linus Torvalds writes:

> It would be very nice to have *two* such commits (either or both of which 
> just disappear), where the top-most is the diff to the index, and the 
> second is the diff from the index to HEAD.

Try this, let me know what you think.  I called the changes in the
working directory "Local uncommitted changes, not checked in to index"
and the changes in the index "Local changes checked in to index but
not committed".  If you prefer some other wording, let me know.  I
made the working directory commit red (as before) and the index commit
magenta, as being between red and blue.  The index commit gets a fake
SHA1 id of 00..001.

Paul.

diff --git a/gitk b/gitk
index 39e452a..7ce86b8 100755
--- a/gitk
+++ b/gitk
@@ -262,11 +262,11 @@ proc chewcommits {view} {
 	set tlimit [expr {[clock clicks -milliseconds] + 50}]
 	set more [layoutmore $tlimit $allread]
 	if {$allread && !$more} {
-	    global displayorder nullid commitidx phase
+	    global displayorder commitidx phase
 	    global numcommits startmsecs
 
 	    if {[info exists pending_select]} {
-		set row [expr {[lindex $displayorder 0] eq $nullid}]
+		set row [first_real_row]
 		selectline $row 1
 	    }
 	    if {$commitidx($curview) > 0} {
@@ -437,6 +437,19 @@ proc readrefs {} {
     }
 }
 
+# skip over fake commits
+proc first_real_row {} {
+    global nullid nullid2 displayorder numcommits
+
+    for {set row 0} {$row < $numcommits} {incr row} {
+	set id [lindex $displayorder $row]
+	if {$id ne $nullid && $id ne $nullid2} {
+	    break
+	}
+    }
+    return $row
+}
+
 # update things for a head moved to a child of its previous location
 proc movehead {id name} {
     global headids idheads
@@ -1871,7 +1884,7 @@ proc showview {n} {
     } elseif {$selid ne {}} {
 	set pending_select $selid
     } else {
-	set row [expr {[lindex $displayorder 0] eq $nullid}]
+	set row [first_real_row]
 	if {$row < $numcommits} {
 	    selectline $row 0
 	} else {
@@ -2643,7 +2656,7 @@ proc layoutmore {tmax allread} {
 
 proc showstuff {canshow last} {
     global numcommits commitrow pending_select selectedline curview
-    global lookingforhead mainheadid displayorder nullid selectfirst
+    global lookingforhead mainheadid displayorder selectfirst
     global lastscrollset
 
     if {$numcommits == 0} {
@@ -2676,7 +2689,7 @@ proc showstuff {canshow last} {
 	if {[info exists selectedline] || [info exists pending_select]} {
 	    set selectfirst 0
 	} else {
-	    set l [expr {[lindex $displayorder 0] eq $nullid}]
+	    set l [first_real_row]
 	    selectline $l 1
 	    set selectfirst 0
 	}
@@ -2700,48 +2713,93 @@ proc doshowlocalchanges {} {
 }
 
 proc dohidelocalchanges {} {
-    global lookingforhead localrow lserial
+    global lookingforhead localfrow localirow lserial
 
     set lookingforhead 0
-    if {$localrow >= 0} {
-	removerow $localrow
-	set localrow -1
+    if {$localfrow >= 0} {
+	removerow $localfrow
+	set localfrow -1
+	if {$localirow > 0} {
+	    incr localirow -1
+	}
+    }
+    if {$localirow >= 0} {
+	removerow $localirow
+	set localirow -1
     }
     incr lserial
 }
 
-# spawn off a process to do git diff-index HEAD
+# spawn off a process to do git diff-index --cached HEAD
 proc dodiffindex {} {
-    global localrow lserial
+    global localirow localfrow lserial
 
     incr lserial
-    set localrow -1
-    set fd [open "|git diff-index HEAD" r]
+    set localfrow -1
+    set localirow -1
+    set fd [open "|git diff-index --cached HEAD" r]
     fconfigure $fd -blocking 0
     filerun $fd [list readdiffindex $fd $lserial]
 }
 
 proc readdiffindex {fd serial} {
-    global localrow commitrow mainheadid nullid curview
+    global localirow commitrow mainheadid nullid2 curview
     global commitinfo commitdata lserial
 
+    set isdiff 1
     if {[gets $fd line] < 0} {
-	if {[eof $fd]} {
-	    close $fd
-	    return 0
+	if {![eof $fd]} {
+	    return 1
 	}
-	return 1
+	set isdiff 0
     }
     # we only need to see one line and we don't really care what it says...
     close $fd
 
-    if {$serial == $lserial && $localrow == -1} {
+    # now see if there are any local changes not checked in to the index
+    if {$serial == $lserial} {
+	set fd [open "|git diff-files" r]
+	fconfigure $fd -blocking 0
+	filerun $fd [list readdifffiles $fd $serial]
+    }
+
+    if {$isdiff && $serial == $lserial && $localirow == -1} {
+	# add the line for the changes in the index to the graph
+	set localirow $commitrow($curview,$mainheadid)
+	set hl "Local changes checked in to index but not committed"
+	set commitinfo($nullid2) [list  $hl {} {} {} {} "    $hl\n"]
+	set commitdata($nullid2) "\n    $hl\n"
+	insertrow $localirow $nullid2
+    }
+    return 0
+}
+
+proc readdifffiles {fd serial} {
+    global localirow localfrow commitrow mainheadid nullid curview
+    global commitinfo commitdata lserial
+
+    set isdiff 1
+    if {[gets $fd line] < 0} {
+	if {![eof $fd]} {
+	    return 1
+	}
+	set isdiff 0
+    }
+    # we only need to see one line and we don't really care what it says...
+    close $fd
+
+    if {$isdiff && $serial == $lserial && $localfrow == -1} {
 	# add the line for the local diff to the graph
-	set localrow $commitrow($curview,$mainheadid)
-	set hl "Local uncommitted changes"
+	if {$localirow >= 0} {
+	    set localfrow $localirow
+	    incr localirow
+	} else {
+	    set localfrow $commitrow($curview,$mainheadid)
+	}
+	set hl "Local uncommitted changes, not checked in to index"
 	set commitinfo($nullid) [list  $hl {} {} {} {} "    $hl\n"]
 	set commitdata($nullid) "\n    $hl\n"
-	insertrow $localrow $nullid
+	insertrow $localfrow $nullid
     }
     return 0
 }
@@ -3338,12 +3396,14 @@ proc drawcmittext {id row col} {
     global commitlisted commitinfo rowidlist parentlist
     global rowtextx idpos idtags idheads idotherrefs
     global linehtag linentag linedtag markingmatches
-    global mainfont canvxmax boldrows boldnamerows fgcolor nullid
+    global mainfont canvxmax boldrows boldnamerows fgcolor nullid nullid2
 
     # listed is 0 for boundary, 1 for normal, 2 for left, 3 for right
     set listed [lindex $commitlisted $row]
     if {$id eq $nullid} {
 	set ofill red
+    } elseif {$id eq $nullid2} {
+	set ofill magenta
     } else {
 	set ofill [expr {$listed != 0? "blue": "white"}]
     }
@@ -4582,16 +4642,19 @@ proc goforw {} {
 }
 
 proc gettree {id} {
-    global treefilelist treeidlist diffids diffmergeid treepending nullid
+    global treefilelist treeidlist diffids diffmergeid treepending
+    global nullid nullid2
 
     set diffids $id
     catch {unset diffmergeid}
     if {![info exists treefilelist($id)]} {
 	if {![info exists treepending]} {
-	    if {$id ne $nullid} {
-		set cmd [concat | git ls-tree -r $id]
+	    if {$id eq $nullid} {
+		set cmd [list | git ls-files]
+	    } elseif {$id eq $nullid2} {
+		set cmd [list | git ls-files --stage -t]
 	    } else {
-		set cmd [concat | git ls-files]
+		set cmd [list | git ls-tree -r $id]
 	    }
 	    if {[catch {set gtf [open $cmd r]}]} {
 		return
@@ -4608,12 +4671,14 @@ proc gettree {id} {
 }
 
 proc gettreeline {gtf id} {
-    global treefilelist treeidlist treepending cmitmode diffids nullid
+    global treefilelist treeidlist treepending cmitmode diffids nullid nullid2
 
     set nl 0
     while {[incr nl] <= 1000 && [gets $gtf line] >= 0} {
-	if {$diffids ne $nullid} {
-	    if {[lindex $line 1] ne "blob"} continue
+	if {$diffids eq $nullid} {
+	    set fname $line
+	} else {
+	    if {$diffids ne $nullid2 && [lindex $line 1] ne "blob"} continue
 	    set i [string first "\t" $line]
 	    if {$i < 0} continue
 	    set sha1 [lindex $line 2]
@@ -4622,8 +4687,6 @@ proc gettreeline {gtf id} {
 		set fname [lindex $fname 0]
 	    }
 	    lappend treeidlist($id) $sha1
-	} else {
-	    set fname $line
 	}
 	lappend treefilelist($id) $fname
     }
@@ -4645,7 +4708,7 @@ proc gettreeline {gtf id} {
 }
 
 proc showfile {f} {
-    global treefilelist treeidlist diffids nullid
+    global treefilelist treeidlist diffids nullid nullid2
     global ctext commentend
 
     set i [lsearch -exact $treefilelist($diffids) $f]
@@ -4653,15 +4716,15 @@ proc showfile {f} {
 	puts "oops, $f not in list for id $diffids"
 	return
     }
-    if {$diffids ne $nullid} {
-	set blob [lindex $treeidlist($diffids) $i]
-	if {[catch {set bf [open [concat | git cat-file blob $blob] r]} err]} {
-	    puts "oops, error reading blob $blob: $err"
+    if {$diffids eq $nullid} {
+	if {[catch {set bf [open $f r]} err]} {
+	    puts "oops, can't read $f: $err"
 	    return
 	}
     } else {
-	if {[catch {set bf [open $f r]} err]} {
-	    puts "oops, can't read $f: $err"
+	set blob [lindex $treeidlist($diffids) $i]
+	if {[catch {set bf [open [concat | git cat-file blob $blob] r]} err]} {
+	    puts "oops, error reading blob $blob: $err"
 	    return
 	}
     }
@@ -4789,11 +4852,13 @@ proc getmergediffline {mdf id np} {
 }
 
 proc startdiff {ids} {
-    global treediffs diffids treepending diffmergeid nullid
+    global treediffs diffids treepending diffmergeid nullid nullid2
 
     set diffids $ids
     catch {unset diffmergeid}
-    if {![info exists treediffs($ids)] || [lsearch -exact $ids $nullid] >= 0} {
+    if {![info exists treediffs($ids)] ||
+	[lsearch -exact $ids $nullid] >= 0 ||
+	[lsearch -exact $ids $nullid2] >= 0} {
 	if {![info exists treepending]} {
 	    gettreediffs $ids
 	}
@@ -4809,22 +4874,41 @@ proc addtocflist {ids} {
 }
 
 proc diffcmd {ids flags} {
-    global nullid
+    global nullid nullid2
 
     set i [lsearch -exact $ids $nullid]
+    set j [lsearch -exact $ids $nullid2]
     if {$i >= 0} {
-	set cmd [concat | git diff-index $flags]
+	if {[llength $ids] > 1 && $j < 0} {
+	    # comparing working directory with some specific revision
+	    set cmd [concat | git diff-index $flags]
+	    if {$i == 0} {
+		lappend cmd -R [lindex $ids 1]
+	    } else {
+		lappend cmd [lindex $ids 0]
+	    }
+	} else {
+	    # comparing working directory with index
+	    set cmd [concat | git diff-files $flags]
+	    if {$j == 1} {
+		lappend cmd -R
+	    }
+	}
+    } elseif {$j >= 0} {
+	set cmd [concat | git diff-index --cached $flags]
 	if {[llength $ids] > 1} {
+	    # comparing index with specific revision
 	    if {$i == 0} {
 		lappend cmd -R [lindex $ids 1]
 	    } else {
 		lappend cmd [lindex $ids 0]
 	    }
 	} else {
+	    # comparing index with HEAD
 	    lappend cmd HEAD
 	}
     } else {
-	set cmd [concat | git diff-tree --no-commit-id -r $flags $ids]
+	set cmd [concat | git diff-tree -r $flags $ids]
     }
     return $cmd
 }
@@ -4834,7 +4918,7 @@ proc gettreediffs {ids} {
 
     set treepending $ids
     set treediff {}
-    if {[catch {set gdtf [open [diffcmd $ids {}] r]}]} return
+    if {[catch {set gdtf [open [diffcmd $ids {--no-commit-id}] r]}]} return
     fconfigure $gdtf -blocking 0
     filerun $gdtf [list gettreediffline $gdtf $ids]
 }
@@ -4877,7 +4961,7 @@ proc getblobdiffs {ids} {
     global diffinhdr treediffs
 
     set env(GIT_DIFF_OPTS) $diffopts
-    if {[catch {set bdf [open [diffcmd $ids {-p -C}] r]} err]} {
+    if {[catch {set bdf [open [diffcmd $ids {-p -C --no-commit-id}] r]} err]} {
 	puts "error getting diffs: $err"
 	return
     }
@@ -5468,7 +5552,7 @@ proc mstime {} {
 
 proc rowmenu {x y id} {
     global rowctxmenu commitrow selectedline rowmenuid curview
-    global nullid fakerowmenu mainhead
+    global nullid nullid2 fakerowmenu mainhead
 
     set rowmenuid $id
     if {![info exists selectedline]
@@ -5477,7 +5561,7 @@ proc rowmenu {x y id} {
     } else {
 	set state normal
     }
-    if {$id ne $nullid} {
+    if {$id ne $nullid && $id ne $nullid2} {
 	set menu $rowctxmenu
 	$menu entryconfigure 7 -label "Reset $mainhead branch to here"
     } else {
@@ -5596,18 +5680,12 @@ proc mkpatchrev {} {
 }
 
 proc mkpatchgo {} {
-    global patchtop nullid
+    global patchtop nullid nullid2
 
     set oldid [$patchtop.fromsha1 get]
     set newid [$patchtop.tosha1 get]
     set fname [$patchtop.fname get]
-    if {$newid eq $nullid} {
-	set cmd [list git diff-index -p $oldid]
-    } elseif {$oldid eq $nullid} {
-	set cmd [list git diff-index -p -R $newid]
-    } else {
-	set cmd [list git diff-tree -p $oldid $newid]
-    }
+    set cmd [diffcmd [list $oldid $newid] -p]
     lappend cmd >$fname &
     if {[catch {eval exec $cmd} err]} {
 	error_popup "Error creating patch: $err"
@@ -7522,6 +7600,8 @@ if {$i >= [llength $argv] && $revtreeargs ne {}} {
 }
 
 set nullid "0000000000000000000000000000000000000000"
+set nullid2 "0000000000000000000000000000000000000001"
+
 
 set runq {}
 set history {}
@@ -7550,7 +7630,8 @@ set stopped 0
 set stuffsaved 0
 set patchnum 0
 set lookingforhead 0
-set localrow -1
+set localirow -1
+set localfrow -1
 set lserial 0
 setcoords
 makewindow

^ permalink raw reply related

* Re: git-svn: failure to clone logcheck's repo
From: Peter Baumann @ 2007-07-13  9:56 UTC (permalink / raw)
  To: git discussion list
In-Reply-To: <20070713094710.GB18199@xp.machine.xx>

On Fri, Jul 13, 2007 at 11:47:10AM +0200, Peter Baumann wrote:
> On Fri, Jul 13, 2007 at 11:36:22AM +0200, martin f krafft wrote:
> > Hi list,
> > 
> > I am trying to access the logcheck repository with git-svn. Even
> > though I can happily clone the trunk:
> > 
> >   git-svn clone svn://svn.debian.org/svn/logcheck/logcheck/trunk git
> > 
> > as soon as I try to clone the entire thing with tags and branches:
> > 
> >   git-svn clone -T trunk -t tags -b branches
> >     svn://svn.debian.org/svn/logcheck/logcheck git
> > 
> > it barfs:
> > 
> >   Initialized empty Git repository in .git/
> >   Using higher level of URL: svn://svn.debian.org/svn/logcheck/logcheck => svn://svn.debian.org/svn/logcheck
> >   r19 = 95d0625b6c62935cfd3e44ec7dca5b6f6b05ab44 (trunk)
> >   [...]
> >   r20 = a3e5240dcc4d064b46273584800d3ae7a1e21330 (trunk)
> >   Found possible branch point: svn://svn.debian.org/svn/logcheck/logcheck/trunk => svn://svn.debian.org/svn/logcheck/logcheck/branches/VENDOR, 20
> >   Found branch parent: (VENDOR) a3e5240dcc4d064b46273584800d3ae7a1e21330
> >   Following parent with do_switch
> >   Successfully followed parent
> >   r21 = 0ffcb70923d77c1d24019362e1ed1cd18d05f235 (VENDOR)
> >   Malformed network data: Malformed network data at /usr/local/bin/git-svn line 1029
> > 
> > I think I'll be fine with *not* preserving logcheck's history when
> > I move it to git, but thought best to let you know since this may be
> > a bug in git-svn.
> > 
> 
> This is a known problem. If I remember correctly,it is save to just
> resume the import with git-svn fetch, and nothing bad should happen.
> 
> Just search the list archive for more info (sorry, I don't have a
> reference to the thread)

Just found it. Look e.g. here on the comment from Eric Wong:

http://thread.gmane.org/gmane.comp.version-control.git/50962/focus=275913

-Peter

^ permalink raw reply

* Re: git-svn: failure to clone logcheck's repo
From: Peter Baumann @ 2007-07-13  9:47 UTC (permalink / raw)
  To: git discussion list
In-Reply-To: <20070713093622.GA21001@lapse.madduck.net>

On Fri, Jul 13, 2007 at 11:36:22AM +0200, martin f krafft wrote:
> Hi list,
> 
> I am trying to access the logcheck repository with git-svn. Even
> though I can happily clone the trunk:
> 
>   git-svn clone svn://svn.debian.org/svn/logcheck/logcheck/trunk git
> 
> as soon as I try to clone the entire thing with tags and branches:
> 
>   git-svn clone -T trunk -t tags -b branches
>     svn://svn.debian.org/svn/logcheck/logcheck git
> 
> it barfs:
> 
>   Initialized empty Git repository in .git/
>   Using higher level of URL: svn://svn.debian.org/svn/logcheck/logcheck => svn://svn.debian.org/svn/logcheck
>   r19 = 95d0625b6c62935cfd3e44ec7dca5b6f6b05ab44 (trunk)
>   [...]
>   r20 = a3e5240dcc4d064b46273584800d3ae7a1e21330 (trunk)
>   Found possible branch point: svn://svn.debian.org/svn/logcheck/logcheck/trunk => svn://svn.debian.org/svn/logcheck/logcheck/branches/VENDOR, 20
>   Found branch parent: (VENDOR) a3e5240dcc4d064b46273584800d3ae7a1e21330
>   Following parent with do_switch
>   Successfully followed parent
>   r21 = 0ffcb70923d77c1d24019362e1ed1cd18d05f235 (VENDOR)
>   Malformed network data: Malformed network data at /usr/local/bin/git-svn line 1029
> 
> I think I'll be fine with *not* preserving logcheck's history when
> I move it to git, but thought best to let you know since this may be
> a bug in git-svn.
> 

This is a known problem. If I remember correctly,it is save to just
resume the import with git-svn fetch, and nothing bad should happen.

Just search the list archive for more info (sorry, I don't have a
reference to the thread)

-Peter

^ permalink raw reply

* git-svn: failure to clone logcheck's repo
From: martin f krafft @ 2007-07-13  9:36 UTC (permalink / raw)
  To: git discussion list

[-- Attachment #1: Type: text/plain, Size: 1525 bytes --]

Hi list,

I am trying to access the logcheck repository with git-svn. Even
though I can happily clone the trunk:

  git-svn clone svn://svn.debian.org/svn/logcheck/logcheck/trunk git

as soon as I try to clone the entire thing with tags and branches:

  git-svn clone -T trunk -t tags -b branches
    svn://svn.debian.org/svn/logcheck/logcheck git

it barfs:

  Initialized empty Git repository in .git/
  Using higher level of URL: svn://svn.debian.org/svn/logcheck/logcheck => svn://svn.debian.org/svn/logcheck
  r19 = 95d0625b6c62935cfd3e44ec7dca5b6f6b05ab44 (trunk)
  [...]
  r20 = a3e5240dcc4d064b46273584800d3ae7a1e21330 (trunk)
  Found possible branch point: svn://svn.debian.org/svn/logcheck/logcheck/trunk => svn://svn.debian.org/svn/logcheck/logcheck/branches/VENDOR, 20
  Found branch parent: (VENDOR) a3e5240dcc4d064b46273584800d3ae7a1e21330
  Following parent with do_switch
  Successfully followed parent
  r21 = 0ffcb70923d77c1d24019362e1ed1cd18d05f235 (VENDOR)
  Malformed network data: Malformed network data at /usr/local/bin/git-svn line 1029

I think I'll be fine with *not* preserving logcheck's history when
I move it to git, but thought best to let you know since this may be
a bug in git-svn.

Cheers,

-- 
martin;              (greetings from the heart of the sun.)
  \____ echo mailto: !#^."<*>"|tr "<*> mailto:" net@madduck
 
spamtraps: madduck.bogus@madduck.net
 
windoze is the one-night-stand of operating systems;
you feel so cheap after having used it.

[-- Attachment #2: Digital signature (GPG/PGP) --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply

* failing to send patches to the list
From: martin f krafft @ 2007-07-13  9:30 UTC (permalink / raw)
  To: git discussion list


[-- Attachment #1.1: Type: text/plain, Size: 937 bytes --]

Dear list,

attached you may find two patches, which I've previously sent to the
list with

  git format-patch -s --stdout | sendmail git@vger.kernel.org

Even though my mail server seems to have delivered them correctly:

  Jul 13 10:53:32 albatross postfix/smtp[29758]: C404D895D6F:
  to=<git@vger.kernel.org>,
  relay=vger.kernel.org[209.132.176.167]:25, delay=2.2,
  delays=0.08/0.04/0.56/1.6, dsn=2.7.1, status=sent (250 2.7.1 Looks
  like Linux source DIFF email.. BF:<H 5.0943e-06>; S932911AbXGMIxb)

they never made it onto the list.

What am I doing wrong?

Should I resubmit the patches or is this enough for now?

-- 
martin;              (greetings from the heart of the sun.)
  \____ echo mailto: !#^."<*>"|tr "<*> mailto:" net@madduck
 
spamtraps: madduck.bogus@madduck.net
 
all software projects are done by iterative prototyping.
some companies call their prototypes "releases", that's all.

[-- Attachment #1.2: 0001-fall-back-to-mozilla-s-sha.h-if-openssl-sha.h-is-not.patch --]
[-- Type: text/x-diff, Size: 1009 bytes --]

From 7b2bb44f78b8bc691bbc1445c6c333fdc281a788 Mon Sep 17 00:00:00 2001
From: martin f. krafft <madduck@madduck.net>
Date: Fri, 13 Jul 2007 11:09:05 +0200
Subject: [PATCH] fall back to mozilla's sha.h if openssl/sha.h is not available

Uses $(CPP) to attempt to preprocess an include <openssl/sha.h> directive. If
that fails, NO_OPENSSL is defined, causing the Makefile to fall back to using
mozilla's SHA implementation.

Signed-off-by: martin f. krafft <madduck@madduck.net>
---
 Makefile |    6 ++++++
 1 files changed, 6 insertions(+), 0 deletions(-)

diff --git a/Makefile b/Makefile
index d7541b4..1676343 100644
--- a/Makefile
+++ b/Makefile
@@ -532,6 +532,12 @@ ifndef NO_CURL
 	endif
 endif
 
+HAS_OPENSSL := $(shell echo "\#include <openssl/sha.h>" \
+			| $(CPP) -o/dev/null - 2>/dev/null || echo no)
+ifeq "$(HAS_OPENSSL)" "no"
+	NO_OPENSSL = "openssl_sha.h_not_found"
+endif
+
 ifndef NO_OPENSSL
 	OPENSSL_LIBSSL = -lssl
 	ifdef OPENSSLDIR
-- 
1.5.3.rc1.7.gcae4


[-- Attachment #1.3: 0002-Provide-stdlayout-option-for-git-svn-to-set-trunk.patch --]
[-- Type: text/x-diff, Size: 2789 bytes --]

From 5f537c75774c5c0b45b74585ace4178738f251f0 Mon Sep 17 00:00:00 2001
From: martin f. krafft <madduck@madduck.net>
Date: Fri, 13 Jul 2007 11:09:46 +0200
Subject: [PATCH] Provide --stdlayout option for git-svn to set trunk,tags,branches to defaults

The --stdlayout option to git-svn init initialises the default Subversion
values of trunk,tags,branches: -T trunk -b branches -t tags. If any of the
-T/-t/-b options are given in addition, they are given preference.

Signed-off-by: martin f. krafft <madduck@madduck.net>
---
 Documentation/git-svn.txt |    6 +++++-
 git-svn.perl              |    8 +++++++-
 2 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt
index 0a210e4..9e74b27 100644
--- a/Documentation/git-svn.txt
+++ b/Documentation/git-svn.txt
@@ -44,10 +44,14 @@ COMMANDS
 --tags=<tags_subdir>;;
 -b<branches_subdir>;;
 --branches=<branches_subdir>;;
+--stdlayout;;
 	These are optional command-line options for init.  Each of
 	these flags can point to a relative repository path
 	(--tags=project/tags') or a full url
-	(--tags=https://foo.org/project/tags)
+	(--tags=https://foo.org/project/tags). The option --stdlayout is
+	a shorthand way of setting trunk,tags,branches as the relative paths,
+	which is the Subversion default. If any of the other options are given
+	as well, they take precedence.
 --no-metadata;;
 	Set the 'noMetadata' option in the [svn-remote] config.
 --use-svm-props;;
diff --git a/git-svn.perl b/git-svn.perl
index b3dffcc..5b443ee 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -77,11 +77,12 @@ my %fc_opts = ( 'follow-parent|follow!' => \$Git::SVN::_follow_parent,
 		   \$Git::SVN::_repack_flags,
 		%remote_opts );
 
-my ($_trunk, $_tags, $_branches);
+my ($_trunk, $_tags, $_branches, $_stdlayout);
 my %icv;
 my %init_opts = ( 'template=s' => \$_template, 'shared:s' => \$_shared,
                   'trunk|T=s' => \$_trunk, 'tags|t=s' => \$_tags,
                   'branches|b=s' => \$_branches, 'prefix=s' => \$_prefix,
+                  'stdlayout' => \$_stdlayout,
                   'minimize-url|m' => \$Git::SVN::_minimize_url,
 		  'no-metadata' => sub { $icv{noMetadata} = 1 },
 		  'use-svm-props' => sub { $icv{useSvmProps} = 1 },
@@ -296,6 +297,11 @@ sub cmd_clone {
 	    $url !~ m#^[a-z\+]+://#) {
 		$path = $url;
 	}
+        if (defined $_stdlayout) {
+          $_trunk = 'trunk' if (!defined $_trunk);
+          $_tags = 'tags' if (!defined $_tags);
+          $_branches = 'branches' if (!defined $_branches);
+        }
 	$path = basename($url) if !defined $path || !length $path;
 	cmd_init($url, $path);
 	Git::SVN::fetch_all($Git::SVN::default_repo_id);
-- 
1.5.3.rc1.7.gcae4


[-- Attachment #2: Digital signature (GPG/PGP) --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply related

* Re: Draft release notes for v1.5.3, as of -rc1
From: Sven Verdoolaege @ 2007-07-13  9:29 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <7vlkdkq00o.fsf_-_@assigned-by-dhcp.cox.net>

On Thu, Jul 12, 2007 at 11:40:39PM -0700, Junio C Hamano wrote:
>   - "git-filter-branch" lets you rewrite the revision history of
>     the current branch, creating a new branch. You can specify a
>     number of filters to modify the commits, files and trees.

Are you sure you want to announce this so publicly given that
Dscho has basically declared git-filter-branch dead?
(I know, I'm mostly to blame.)

skimo

^ permalink raw reply

* Re: [PATCH] make git-send-email.perl handle email addresses with no names when Email::Valid is present
From: Stephen Rothwell @ 2007-07-13  8:28 UTC (permalink / raw)
  To: Greg KH; +Cc: Junio C Hamano, git
In-Reply-To: <20070713063414.GN24317@kroah.com>

[-- Attachment #1: Type: text/plain, Size: 1138 bytes --]

On Thu, 12 Jul 2007 23:34:14 -0700 Greg KH <greg@kroah.com> wrote:
>
> On Thu, Jul 12, 2007 at 10:47:17PM -0700, Junio C Hamano wrote:
> > Greg KH <greg@kroah.com> writes:
> > 
> > > When using git-send-email.perl on a changeset that has:
> > > 	Cc: <stable@kernel.org>
> > > in the body of the description, and the Email::Valid perl module is
> > > installed on the system, the email address will be deemed "invalid" for
> > > some reason (Email::Valid isn't smart enough to handle this?) and
> > > complain and not send the address the email.
> > 
> > That appears to be the case.
> > 
> >         bad foo
> >         bad <foo@bar.baz>
> >         ok  foo@bar.baz
> >         ok  Foo <foo@bar.baz>

This would be a bug in Email::Valid as it complains that the second
address fails the rfc822 check, however rfc822 says that the "display
name" before the '<' is optional.

> > I'd probably do:
> > 
> > 	if ($have_email_valid) {
> > 		$address =~ s/^<(.*)>$/$1/;

	$address =~ s/^\s*<(.*)>\s*$/$1/;

-- 
Cheers,
Stephen Rothwell                    sfr@canb.auug.org.au
http://www.canb.auug.org.au/~sfr/

[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply

* Re: git-update-server-info may be required,cannot clone and pull from a remote repository
From: pradeep singh @ 2007-07-13  8:27 UTC (permalink / raw)
  To: Jakub Narebski; +Cc: Eric Wong, git
In-Reply-To: <200707130217.37817.jnareb@gmail.com>

Thank you all for your valuable suggestions and timely help.
I am now able to view the git repo in a web browser.

Thank you once again for help and for a great tool like git.
--pradeep


On 7/13/07, Jakub Narebski <jnareb@gmail.com> wrote:
> Pradeep Singh wrote:
> > On 7/12/07, Jakub Narebski <jnareb@gmail.com> wrote:
> >> [Cc: git@vger.kernel.org, Pradeep Singh <pradeep.rautela@gmail.com>,
> >>  Eric Wong <normalperson@yhbt.net> (instaweb creator)]
> >>
> >> pradeep singh wrote:
> >>
> >>> Anyway i could not get gitweb running after running git-instaweb.
> >>>
> >>> Any thoughts on how to setup a gitweb interface ?
> >>
> >> What information does gitweb/INSTALL lack?
> >
> > May be I am running some old version on my Ubuntu Edgy machine
> > perhaps? I cannot find such a file anywhere?
>
> You can always look up this file at git.git gitweb (either kernel.org
> one, or repo.or.cz one), e.g.
>
>   http://repo.or.cz/w/alt-git.git/:gitweb/INSTALL
>
> > Looks like it is available in newer versions.
> > Does it works for git-1.4.4?
>
> The installation instructions may have changed a bit since then. It is
> much easier to use 1.5.x series, nevertheless.
>
> --
> Jakub Narebski
> Poland
>


-- 
Pradeep

^ permalink raw reply

* Re: [PATCH 6/6] Add git-rewrite-commits
From: Sven Verdoolaege @ 2007-07-13  8:01 UTC (permalink / raw)
  To: git, Junio C Hamano; +Cc: Johannes Schindelin
In-Reply-To: <11842671631635-git-send-email-skimo@liacs.nl>

On Thu, Jul 12, 2007 at 09:06:03PM +0200, skimo@liacs.nl wrote:
> +static int rewrite_parents(struct commit *commit, int path_pruning)
> +{
> +	int n;
> +	struct commit_list *list, *parents, **prev;
> +	unsigned char sha1[20];
> +
> +	for (n = 0, prev = &commit->parents; *prev; ++n) {
> +		list = *prev;
> +
> +		rewrite_parents(list->item, path_pruning);
> +		if (!is_pruned(list->item, path_pruning)) {
> +			prev = &list->next;
> +			continue;
> +		}

Oops... that should be the other way around...

diff --git a/builtin-rewrite-commits.c b/builtin-rewrite-commits.c
index d95a16c..4cd17ae 100644
--- a/builtin-rewrite-commits.c
+++ b/builtin-rewrite-commits.c
@@ -279,11 +279,11 @@ static int rewrite_parents(struct commit *commit, int path_pruning)
 	for (n = 0, prev = &commit->parents; *prev; ++n) {
 		list = *prev;
 
-		rewrite_parents(list->item, path_pruning);
 		if (!is_pruned(list->item, path_pruning)) {
 			prev = &list->next;
 			continue;
 		}
+		rewrite_parents(list->item, path_pruning);
 
 		hashcpy(sha1, list->item->object.sha1);
 		get_rewritten_sha1(sha1);

I'll include it in my next version.

skimo

^ permalink raw reply related

* Re: pull-fetch-param.txt
From: Gerrit Pape @ 2007-07-13  7:48 UTC (permalink / raw)
  To: Junio C Hamano, git
In-Reply-To: <7vejjcpyb5.fsf@assigned-by-dhcp.cox.net>

On Fri, Jul 13, 2007 at 12:17:34AM -0700, Junio C Hamano wrote:
> Gerrit Pape <pape@smarden.org> writes:
> >> Quick question.  Is the build done with "make
> >> ASCIIDOC8=YesPlease"?
> >
> > No, must have missed that.  This solves the first issue ...
> > ..., but not the second one with callout lists.
> 
> Sorry, does not reproduce for me, with asciidoc 8.2.1.  There
> must be something different between our environments.

Yes, I have docbook-xsl 1.72.0.

> Here is an excerpt from what I get for git-reset.txt in
> git-reset.1:
> 
> -- >8 --
> .\"     Title: git\-reset
> .\"    Author: 
> .\" Generator: DocBook XSL Stylesheets v1.71.0 <http://docbook.sf.net/>
> .\"      Date: 07/13/2007
> .\"    Manual: Git Manual
> .\"    Source: Git 1.5.3.rc1.4.gaf83
> 
>     ...
> 
> .SH "EXAMPLES"
> .PP
> Undo a commit and redo
> .RS 3n
> .sp
> .RS 3n
> .nf
> $ git commit ...
> $ git reset \-\-soft HEAD^      \fB(1)\fR
> $ edit                        \fB(2)\fR
> $ git commit \-a \-c ORIG_HEAD  \fB(3)\fR

I get the same with docbook-xsl 1.71.0, but with 1.72.0:

.\"     Title: git-reset
.\"    Author: 
.\" Generator: DocBook XSL Stylesheets v1.72.0 <http://docbook.sf.net/>
.\"      Date: 07/13/2007
.\"    Manual: Git Manual
.\"    Source: Git 1.5.3.rc0.104.g71f98
[...]
.SH "EXAMPLES"
.PP
Undo a commit and redo
.RS 4
.sp
.RS 4
.nf
$ git commit ...
$ git reset \-\-soft HEAD^      \efB(1)\efR
$ edit                        \efB(2)\efR
$ git commit \-a \-c ORIG_HEAD  \efB(3)\efR
.fi
.RE
\&.sp
\efB1. \efRThis is most often done when you remembered what you just committed is incomplete, or you misspelled your commit message, or both. Leaves working tree as it was before "reset".

\&.br
\efB2. \efRmake corrections to working tree files.

\&.br
\efB3. \efR"reset" copies the old head to .git/ORIG_HEAD; redo the commit by starting with its log message. If you do not need to edit the message further, you can give \-C option instead.


I'll check with the docbook-xsl Debian maintainer.

Thanks, Gerrit.

^ permalink raw reply

* Re: pull-fetch-param.txt
From: Junio C Hamano @ 2007-07-13  7:17 UTC (permalink / raw)
  To: Gerrit Pape; +Cc: git
In-Reply-To: <20070713055346.634.qmail@1e54e4f4e1041d.315fe32.mid.smarden.org>

Gerrit Pape <pape@smarden.org> writes:

>> Quick question.  Is the build done with "make
>> ASCIIDOC8=YesPlease"?
>
> No, must have missed that.  This solves the first issue ...
> ..., but not the second one with callout lists.

Sorry, does not reproduce for me, with asciidoc 8.2.1.  There
must be something different between our environments.

Here is an excerpt from what I get for git-reset.txt in
git-reset.1:

-- >8 --
.\"     Title: git\-reset
.\"    Author: 
.\" Generator: DocBook XSL Stylesheets v1.71.0 <http://docbook.sf.net/>
.\"      Date: 07/13/2007
.\"    Manual: Git Manual
.\"    Source: Git 1.5.3.rc1.4.gaf83

    ...

.SH "EXAMPLES"
.PP
Undo a commit and redo
.RS 3n
.sp
.RS 3n
.nf
$ git commit ...
$ git reset \-\-soft HEAD^      \fB(1)\fR
$ edit                        \fB(2)\fR
$ git commit \-a \-c ORIG_HEAD  \fB(3)\fR
.fi
.RE
.sp
\fB1. \fRThis is most often done when you remembered what you just committed is incomplete, or you misspelled your commit message, or both. Leaves working tree as it was before "reset".
.br
\fB2. \fRmake corrections to working tree files.
.br
\fB3. \fR"reset" copies the old head to .git/ORIG_HEAD; redo the commit by starting with its log message. If you do not need to edit the message further, you can give \-C option instead.

See also the \-\-amend option to \fBgit\-commit\fR(1).
.br
.RE
-- 8< --

and "man -l git-reset.1" seems to show the callouts just fine.

-- >8 --
 ...
EXAMPLES
       Undo a commit and redo

             $ git commit ...
             $ git reset --soft HEAD^      (1)
             $ edit                        (2)
             $ git commit -a -c ORIG_HEAD  (3)

          1. This is most often done when you remembered what you just
          committed is incomplete, or you misspelled your commit message,
          or both. Leaves working tree as it was before "reset".
          2. make corrections to working tree files.
          3. "reset" copies the old head to .git/ORIG_HEAD; redo the
          commit by starting with its log message. If you do not need to
          edit the message further, you can give -C option instead.

          See also the --amend option to git-commit(1).
-- 8< --

^ permalink raw reply

* Re: Perforce support.
From: Alex Riesen @ 2007-07-13  6:45 UTC (permalink / raw)
  To: Govind Salinas; +Cc: git
In-Reply-To: <69b0c0350707121134t61905e87xaa2fc78c30891bb0@mail.gmail.com>

[-- Attachment #1: Type: text/plain, Size: 2699 bytes --]

On 7/12/07, Govind Salinas <govindsalinas@gmail.com> wrote:
> I am hoping to convince my co-workers to start using a Distributed
> SCM, hopefully git, and I wanted to see what people had to say about
> the Perforce-git interoperability.  To make it more fun we are doing
> this on Windows.

There are two scripts. git-p4import.py, is in standard distribution,
and is AFAIK not used very much Windows. The other, git-p4-import.bat
is never tried on anywhere but Windows and is maintained outside
of mainstream Git.

I maintain the second one, git-p4-import.bat. I don't dare to submit
it for distribution with Git: it is very centered on stu^H^H^H^H suboptimal
practices I have at work.

> I have been playing around with git for a month or so and have started
> writing, what I hope will be, a nice GUI over git that works well on
> Windows (Cygwin) and offers some feeling of familiarity to our
> Perforce users.  That however is only half the problem.

I trust you already seen git gui...

> We need to be able to go back and forth to our main Perforce depot,
> and while I understand that git-svn support is very good, I have only
> seen limited support of Perforce.

Because Perforce is, in some respects, even worse: it is closed.
It will never get the level of support SVN or even CVS have.

>   I was wondering if anyone has been
> using git with p4 and how well did it work.  We have very complex and
> somewhat large "clients" that do a lot of mapping of directories
> (which strikes me as particularly insane) and I was wondering if any
> of the tools support that.

I am in such situation. Perforce is so bad in managing it,
that people here had to write a handful of wrappers around
standard commands just to make it limping (and it is falling
apart right now).

I use the git-p4-import.bat to import and export the commits.
It is PITA though. Try it (attached), but don't expect too much.
It is a perl script, geared towards Windows and Activision Perl
(hopefully not too much and it still can work with cygwin's perl).
Some examples (I assume a useful shell, not cmd):

Import a range of P4 changelists, convert p4 path to local path

  $ git-p4-import.bat --p4-path //Very/stupid/perforce \
  --local-path "" --p4-range '@123,456'
Result can be found in the reference named p4/IMPORT.

Export some commits:

  $ git-p4-import.bat --merge my-branch -y --edit=vim
This prepare a change lists, let you edit the changelist description
in vim and submit it. I suggest you always rebase my-branch
before this: p4 linearizes the history, so in case you loose your
original git branch, the _useful_ parenthood information is lost with it.
Better not even have any hopes associated with this.

[-- Attachment #2: git-p4-import.bat.txt --]
[-- Type: text/plain, Size: 50773 bytes --]

@rem = 'NT: CMD.EXE vim: syntax=perl noet sw=4
@perl -x -s %0 -- %*
@goto __end_of_file__
@rem ';
#!perl -w
#line 7

local $VERBOSE = 0;
local $DRYRUN = 0;
local $SHOW_DIFFS = 0;
local $AUTO_COMMIT = 0;
local $JUST_COMMIT = 0;
local $GIT_DIR = undef;
local $P4CLIENT = undef;
local $EDIT_COMMIT = 0;
local $FORCE = 0;
local @FULL_IMPORT = 0;
local @DESC = ();
local $SPEC = undef;
local $P4HAVE_FILE = undef;
local %P4USERS = ();
local $FULL_DESC = 1;
local $HEAD_FROM_P4 = 0;
local $P4_EDIT_MERGE = 0;
local $P4_EDIT_CHANGED = 0;
local $P4_EDIT_HEAD = undef;
local $editor = undef;

local $P4_PATH = undef;
local $P4_RANGE = undef;
local $P4_LOCAL_PATH = undef;
local $START_POINT = undef;
local $UPDATE_BRANCH = undef;
local $P4_TAG = undef;

local $P4_SYNC_TO = undef;
# cache for refs/p4import references
local %P4IMPORT_HEADS = ();

sub read_args {
    my $no_opt = 0;
    my $no_EDIT_COMMIT = 0;
    while (my $f = shift) {
	goto _files if $no_opt;
	$no_opt=1, next if $f eq '--';

	$DRYRUN=1, next if $f eq '-n' or $f eq '--dry-run';
	$SHOW_DIFFS=$DRYRUN=1, next if $f eq '--diffs';
	$AUTO_COMMIT=1, next if $f eq '-y' or $f eq '--yes';
	$JUST_COMMIT=1, next if $f =~ /^--(just-)?commit$/;
	$EDIT_COMMIT=1, next if $f eq '-e' or $f eq '--edit';
	$no_EDIT_COMMIT=1, next if $f eq '-ne' or $f eq '--no-edit';
	$FORCE=1, next if $f eq '--force';
	if ($f =~ /^-(e|-edit=)(.*)/) {
	    $editor = $2;
	    $EDIT_COMMIT=1;
	    next;
	}
	$FULL_IMPORT=1, next if $f eq '--full';
	$FULL_DESC++, next if $f eq '--p4-desc';

	$VERBOSE++, next if $f eq '-v' or $f eq '--verbose';
	$P4CLIENT = shift, next if $f eq '--client' and @_;
	$P4CLIENT = $1, next if $f =~ /^--client=(.*)/;
	push(@DESC,'c'.shift), next if $f eq '-C' and @_;
	push(@DESC,"c$1"), next if $f =~ /^--changelist=(.*)/;
	push(@DESC,'f'.shift), next if $f eq '-F' and @_;
	push(@DESC,"f$1"), next if $f =~ /^--file=(.*)/;
	push(@DESC,'4'.shift), next if ($f eq '--ptr' or $f eq '--p4') and @_;
	push(@DESC,"4$2"), next if $f =~ /^--(p4|ptr)=(.*)/;

	$P4_EDIT_CHANGED=1, next if $f eq '--p4-edit-changed';
	if ($f =~ /^--p4-edit-changed=(.*)/) {
	    $P4_EDIT_CHANGED=1;
	    $P4_EDIT_HEAD=$1;
	    next 
	}
	if ($f =~ /^--merge=(.*)/ or
	    ($f eq '--merge' and @_)) {
	    $P4_EDIT_MERGE=1;
	    $P4_EDIT_CHANGED=1;
	    $EDIT_COMMIT=1;
	    $P4_EDIT_HEAD= $f eq '--merge' ? shift: $1;
	    next 
	}
	$P4_TAG=$1, next if $f =~ /^--p4-tag=(.+)/;
	$P4_TAG=shift, next if $f eq '--p4-tag' and @_;
	# $P4_EDIT_MERGE = 'submit' if $f eq '--submit';

	$P4_PATH=$1, next if $f =~ /^--p4-path=(.*)/;
	$P4_PATH=shift, next if $f eq '--p4-path' and @_;
	$P4_RANGE=$1, next if $f =~ /^--p4-range=(.*)/;
	$P4_RANGE=shift, next if $f eq '--p4-range' and @_;
	$P4_LOCAL_PATH=$1, next if $f =~ /^--local-path=(.*)/;
	$P4_LOCAL_PATH=shift, next if $f eq '--local-path' and @_;
	$START_POINT=$1, next if $f =~ /^--start=(.*)/;
	$START_POINT=shift, next if $f eq '--start' and @_;
	$UPDATE_BRANCH=$1, next if $f =~ /^--branch=(.*)/;
	$UPDATE_BRANCH=shift, next if $f eq '--branch' and @_;

	$P4_SYNC_TO=$1, next if $f =~ /--sync=(.*)/;
	$P4_SYNC_TO=shift, next if $f eq '--sync' and @_;

	if ($f eq '--help' or $f eq '-h') {
	    print <<EOF;
$0 [-n|--dry-run] [-y|--yes] [--client <client-name>] [--diffs] \
[-e|--edit] [--just-commit] [--full] [-v|--verbose] [-C <change-number>] \
[-F <filename>] [--ptr|--p4 <p4-path-and/or-revision>] [--p4-desc] \
[--] [<specification>]

Perforce client state importer. Creates a git commit on the current
branch from a state the given p4 client and working directory hold.

<specification> must be given and is expected to be a file which will be
stored on the side branch under the name "spec".
Remote-to-local mapping and the revisions of files are stored in "have",
and the client definition - in "client".

--client client Specify client name (saved in .git/p4/client for the next time)
--full          Perform full import, don't even try to figure out what changed
-y|--yes        Commit automatically (by default only index updated)
--just-commit   To be used after you forgot to run with --yes first time
-n|--dry-run    Do not update the index and do not commit
-e|--edit       Edit commit description before committing
-v|--verbose    Be more verbose. Can be given many times, increases verbosity

--file=file
-F file         Take description for the commit from a file in the
                next parameter

--changelist=change
-C change       Take description for the commit from this p4 change

--p4|--ptr=p4-path-and/or-revision
                Take description for the commit from the p4 change described
                by this p4 path, possibly including revision specification

--p4-path=p4-path
                Import changes directly from Perforce concerning the given
                p4-path. The first change list defines the starting state
                of imported path
--p4-range=revrange
                Restrict the changes to the revrange change lists.
                The first change list is always imported fully,
                all the subsequent - incrementally. If not given -
                all changes on the patch will be imported
--local-path=local-path
                Replace the p4-path given to --p4-path with local-path
                in the imported pathnames
--start=git-ref
                Start commiting at the given reference. If not given,
                the value of reference given to --branch is used,
                otherwise the current HEAD is assumed
--branch=git-branch
                After everything is imported and committed, update the
                reference git-branch with sha1 of the last commit.
                If not given - the last commit of the import is stored
                in the reference /p4/IMPORT

--p4-desc       Increase amount of junk from p4 change description

--diffs         Show files which are different between local filesystem, index,
                and the current HEAD. Does not do anything else

--merge=branch
                Merge with the given sha1 and prepare a p4 submission.
                Current HEAD will be git-merged with branch.
                The working directory must have no local changes

--p4-edit-changed
--p4-edit-changed=branch
                Read the given branch in the index and prepare a p4 submission.
                Current HEAD must fast-forward to sha1. If branch is omited,
                the current HEAD is assumed (useful after push on HEAD).
                The working directory must have no local changes

--sync=ref
                Sync the current client to the state given by ref:head
                file. The file must have been stored by previous import.
                The option considers all references under p4import when
                looking for the "have"-file


The descriptions taken from p4 changes given by -C and --p4 will
be concatenated if the options given multiple times.
"--" can be used to separate options from description files.

EOF
	    exit(0);
	}
	die "$0: unknown option $f\n" if $f =~ /^-/;
    _files:
	warn "$0: spec was already set, $SPEC ignored\n" if defined($SPEC);
	$SPEC = $f;
    }
    $EDIT_COMMIT = 0 if $no_EDIT_COMMIT;
}
read_args(@ARGV);

$editor = $ENV{VISUAL} unless defined($editor);
$editor = $ENV{EDITOR} unless defined($editor);
$editor = 'vim' unless defined($editor);
die "$0: no editor defined\n" if $EDIT_COMMIT and !defined($editor);

if (!defined($GIT_DIR)) {
    $GIT_DIR = git_rev_parse('--git-dir');
    die "$0: GIT_DIR not found\n" if !defined($GIT_DIR) or !-d $GIT_DIR;
}

exit(git_show_diffs() ? 1: 0) if $SHOW_DIFFS;

# the most commands below need GIT_DIR/p4, try to create it
mkdir "$GIT_DIR/p4", 0775;

if (defined($P4_PATH)) {
    $START_POINT = $UPDATE_BRANCH
	if !defined($START_POINT) and defined($UPDATE_BRANCH);
    my $parent = undef;
    $parent = git_rev_parse($START_POINT) if defined($START_POINT);
    $parent = '' if !defined($parent);

    $P4_RANGE = '#head' if !defined($P4_RANGE); # import all changes
    $P4_LOCAL_PATH = $P4_PATH if !defined($P4_LOCAL_PATH);
    $P4_LOCAL_PATH =~ s!^/+!!o;
    s!/+\.\.\.$!!o for ($P4_PATH,$P4_LOCAL_PATH);
    s!/+$!!o for ($P4_PATH,$P4_LOCAL_PATH);
    print "Path conversion:\n'$P4_PATH' ->\n'$P4_LOCAL_PATH'\n" if $VERBOSE > 1;
    import_p4_dir($P4_PATH,$P4_LOCAL_PATH,$P4_RANGE,$parent,$UPDATE_BRANCH);
    exit 0;
}

if (defined($P4_TAG)) {
    my $rc = git_p4_tag_HEAD($P4_TAG);
    exit 127 if $rc & 0xff;
    exit 1 if $rc;
    exit 0;
}

# P4 client was given in command-line. Store it
if ( defined($P4CLIENT) ) {
    mkdir "$GIT_DIR/p4", 0777;
    if ( open(F, '>', "$GIT_DIR/p4/client") ) {
	print F "$P4CLIENT\n";
	close(F);
    } else {
	die "$0: cannot store client name: $!\n"
    }
} else {
    if ( open(F, '<', "$GIT_DIR/p4/client") ) {
	($P4CLIENT) = <F>;
	close(F);
	$P4CLIENT =~ s/^\s*//,$P4CLIENT =~ s/\s*$// if defined($P4CLIENT);
    }
}
die "P4 client not defined\n" if !defined($P4CLIENT) or !length($P4CLIENT);
print "reading client $P4CLIENT\n" if $VERBOSE;
local ($P4ROOT, $p4clnt, $P4HOST);
open(my $fdo, '>', "$GIT_DIR/p4/client.def") or die "p4/client.def: $!\n";
binmode($fdo);
open(my $fdi, '-|', "p4 client -o $P4CLIENT") or die "p4 client: $!\n";
binmode($fdi);
my $last_line_len = 0;
while (<$fdi>) {
    next if /^#/o;
    if ( m/^\s*Root:\s*(\S+)[\\\/]*\s*$/so ) { $P4ROOT = $1 }
    elsif ( m/^\s*Client:\s*(\S+)/o ) { $p4clnt = $1 }
    elsif ( m/^\s*Host:\s*(\S+)/o ) { $P4HOST = $1 }
    ($VERBOSE and print), next if /^(Access|Update):/;
    s/\r?\n$//so;
    my $len = length($_);
    print $fdo "$_\n" if $len or $len != $last_line_len;
    $last_line_len = $len;
}
close($fdi);
close($fdo);

die "Client root not defined\n" unless defined($P4ROOT);
if ( $VERBOSE ) {
    use Cwd;
    print "GIT_DIR: $GIT_DIR\n";
    print "Root: $P4ROOT (cwd: ".cwd()."\n";
    print "Host: $P4HOST\n";
    print "Client: $p4clnt\n" if $p4clnt ne $P4CLIENT;
}

die if defined $P4_SYNC_TO;
#exit(git_sync_to($P4_SYNC_TO)) if defined $P4_SYNC_TO;

if ($P4_EDIT_CHANGED) {
    exit(git_p4_merge($P4_EDIT_HEAD, $P4_EDIT_MERGE, $AUTO_COMMIT));
}

my ($git_head,$git_p4_head,$git_p4_have) = git_p4_init();

if ($JUST_COMMIT) {
    git_p4_commit($git_head, $git_p4_head);
    exit 0;
}

local %gitignore_dirs = ();
$gitignore_dirs{'/'} = read_filter_file("$GIT_DIR/info/exclude");
push(@{$gitignore_dirs{'/'}}, @{read_filter_file('.gitignore')});

my %git_index = ();
$/ = "\0";
my @git_X = ();
print "Reading git file list(git ls-files @git_X --cached -z)...\n" if $VERBOSE;
foreach ( qx{git ls-files @git_X --cached -z} ) {
    chop; # chop \0
    next if m/^\.gitignore$/o;
    next if m/\/\.gitignore$/o;
    next if filtered($_);
    $git_index{$_} = 1;
}

my @git_add = ();
my @git_addx = ();
my @git_del = ();
my @git_upd = ();

print "Reading P4 file list...\n" if $VERBOSE;
local ($Conflicts,$Ignored,$Added,$Deleted,$Updated) = (0,0,0,0,0);
$/ = "\n";
my $in_name = 0;
my @root = split(/[\/\\]+/, $P4ROOT);
my %p4_index = ();
my %p4_a_lc = ();
my %lnames = ();
my %lconflicts = ();
if (opendir(DIR, '.')) {
    $lnames{'.'} = [grep {$_ ne '.' and $_ ne '..'} readdir(DIR)];
    closedir(DIR);
}
open(my $have, "p4 -G -c $P4CLIENT -H $P4HOST -d $P4ROOT have |") or
    die "$0: failed to start p4: $!\n";
binmode($have);
$P4HAVE_FILE = "$GIT_DIR/p4/have";
open(my $storedhave, '>', $P4HAVE_FILE) or die "$P4HAVE_FILE: $!\n";
binmode($storedhave);
my ($cnt,$err,$ent) = (0,0,undef);
while (defined($ent=read_pydict_entry($have))) {
    if (defined($ent->{code}) and defined($ent->{data})) {
	++$err if $ent->{code} eq 'error';
	print STDERR 'p4: '.$ent->{code}.': '.$ent->{data}."\n";
	next;
    }
    next if !defined($ent->{depotFile}) or !defined($ent->{clientFile});
    ++$cnt;
    my $a = $ent->{depotFile};
    $ent->{clientFile} =~ m!^//[^/]+/(.*)!o;
    my $b = $1;
    my @bb = split(/\/+/, $b);
    print $storedhave "$a\0$ent->{clientFile}\0$ent->{haveRev}\0\n";

    if ( $^O eq 'MSWin32' ) {
	# stupid windows, daft activestate, dumb P4
	# This piece below is checking for file name conflicts
	# which happen on windows because of it mangling the names.
	my $blc = lc $b;
	if ( $#bb > 0 ) {
	    my $path = '.';
	    foreach my $n (@bb[0 .. $#bb -1]) {
		my @conflicts =
		    grep {lc $_ eq lc $n and $_ ne $n} @{$lnames{$path}};
		if (@conflicts and !exists($lconflicts{"$path/$n"})) {
		    warn "warning: $a -> $b\n".
			 "warning: conflict between path \"$path/$n\" and ".
			 "local filesystem in \"@conflicts\"\n";
		    $Conflicts++;
		    $lconflicts{"$path/$n"} = 1;
		}
		$path .= "/$n";
		if (!exists($lnames{$path})) {
		    if (opendir(DIR, $path)) {
			$lnames{$path} =
			    [grep {$_ ne '.' and $_ ne '..'} readdir(DIR)];
			closedir(DIR);
			#print "read $path (",scalar(@{$lnames{$path}}),")\n";
		    }
		}
	    }
	}
	if (!exists($p4_a_lc{$blc})) {
	    $p4_a_lc{$blc} = [$a, $b];
	} else {
	    warn("warning: $a -> $b\n".
		 "warning: conflicts with ".
		 $p4_a_lc{$blc}->[0]." -> ".
		 $p4_a_lc{$blc}->[1]."\n");
	    $Conflicts++;
	    next;
	}
    }

    my $i;
    for ($i = 0; $i < $#bb; ++$i) {
	my $bdir = join('/',@bb[0 .. $i]) . '/';
	if ( !exists($gitignore_dirs{$bdir}) ) {
	    $gitignore_dirs{$bdir} = read_filter_file("$bdir.gitignore");
	}
    }
    if (filtered($b)) {
	print " i $b\n" if $VERBOSE > 3;
	$Ignored++;
	next
    }
    $p4_index{$b} = $a;
    if ( exists($git_index{$b}) ) {
	my $needup = 1;
	if (defined($git_p4_have)) {
	    $prev = $git_p4_have->{$a};
	    if (defined($prev)) {
		$prev->[0] =~ m!^//[^/]+/(.*)!o;
		$needup = 0 if ($b eq $1) and ($prev->[1] eq $ent->{haveRev});
		if ($needup and $VERBOSE > 1) {
		    my $reason;
		    $reason = 'local file' if $b ne $1;
		    $reason = 'revision' if $prev->[1] ne $ent->{haveRev};
		    print "$a ($reason changed)\n";
		}
	    }
	}
	if ($needup) {
	    $Updated++;
	    push(@git_upd, $b);
	}
    } else {
	$Added++;
	if ( $b =~ m/\.(bat|cmd|pl|sh|exe|dll)$/io )
	{ push(@git_addx, $b) } else { push(@git_add, $b) }
    }
}
close($storedhave);
close($have);
exit 1 if $err; # the error already reported
die "Nothing in the client $P4CLIENT\n" if !$cnt;

undef %p4_a_lc;

@git_del = grep { !exists($p4_index{$_}) } keys %git_index;
$Deleted = $#git_del + 1;

#foreach (keys %git_index)
#{ push(@git_del, $_) if !exists($p4_index{$_}) }

if ( $DRYRUN ) {
    print($#git_add+$#git_addx+ 2," files to add\n") if $VERBOSE;
    print map {" a $_\n"} @git_add if $VERBOSE > 2;
    print map {" a $_\n"} @git_addx if $VERBOSE > 2;
    print($#git_del+1," files to unreg\n") if $VERBOSE;
    print map {" d $_\n"} @git_del if $VERBOSE > 2;
    print($#git_upd+1," files to update\n") if $VERBOSE;
    print map {" u $_\n"} @git_upd if $VERBOSE > 2;
    print "added: $Added, unregd: $Deleted, updated: $Updated, ignored: $Ignored";
    print ", conflicts: $Conflicts" if $Conflicts;
    print "\n";
} else {
    if (@git_add || @git_addx) {
	print($#git_add+$#git_addx+ 2,
	      " files | git update-index --add -z --stdin\n")
	    if $VERBOSE;
	if (@git_add) {
	    open(GIT, '| git update-index --add --chmod=-x -z --stdin') or
		die "$0 git-update-index(add): $!\n";
	    print GIT map {print " a $_\n" if $VERBOSE > 1; "$_\0"} @git_add;
	    close(GIT);
	}
	if (@git_addx) {
	    open(GIT, '| git update-index --add --chmod=+x -z --stdin') or
		die "$0 git-update-index(add): $!\n";
	    print GIT map {print " a $_\n" if $VERBOSE > 1; "$_\0"} @git_addx;
	    close(GIT);
	}
    }

    if (@git_del) {
	print($#git_del+1," files | git update-index --remove -z --stdin\n")
	    if $VERBOSE;
	open(GIT, '| git update-index --force-remove -z --stdin') or
	    die "$0 git-update-index(del): $!\n";
	print GIT map {print " d $_\n" if $VERBOSE > 1; "$_\0"} @git_del;
	close(GIT);
    }

    if (@git_upd) {
	print($#git_upd+1," files | git update-index -z --stdin\n")
	    if $VERBOSE;
	open(GIT, '| git update-index -z --stdin') or
	    die "$0 git-update-index(upd): $!\n";
	print GIT map {print " u $_\n" if $VERBOSE > 1; "$_\0"} @git_upd;
	close(GIT);
    }

    print "added: $Added, unregd: $Deleted, updated: $Updated, ignored: $Ignored";
    print ", conflicts: $Conflicts" if $Conflicts;
    print "\n";
    git_p4_commit($git_head, $git_p4_head) if $AUTO_COMMIT;
}

exit 0;

sub run_or_exit {
    my $rc = system(@_);
    exit(127) if $rc & 0xff;
    exit(1) if $rc;
    return 0;
}

sub filtered {
    my $name = shift;
    study($name);
    my @path = split(/\/+/o, $name);
    my $dir = '';
    $name = '';

    foreach my $d (@path) {
	$name .= $d;
#	print STDERR "$dir: $name $d\n" if $v;
	foreach my $re (@{$gitignore_dirs{'/'}}) {
	    return 1 if $name =~ m/$re/;
	    return 1 if $d =~ m/$re/;
	}
	if ( length($dir) and exists($gitignore_dirs{$dir}) ) {
	    foreach my $re (@{$gitignore_dirs{$dir}}) {
		return 1 if $name =~ m/$re/;
		return 1 if $d =~ m/$re/;
	    }
	}
	$name .= '/';
	$dir = $name;
    }
#    print STDERR "$name not filtered\n" if $v;
    return 0;
}

sub read_filter_file {
    my @filts = ();
    my $file = shift;
    if ( open(my $if, '<', $file) ) {
	print "added ignore file $file\n" if $VERBOSE;
	$/ = "\n";
	while (my $l = <$if>) {
	    next if $l =~ /^\s*#/o;
	    next if $l =~ /^\s*$/o;
	    $l =~ s/[\r\n]+$//so;
	    $l =~ s/\./\\./go;
	    $l =~ s/\*/.*/go;
	    if ( $l =~ m/\// ) {
		$l = "^$l($|/)";
	    } else {
		$l = "(^|/)$l\$";
	    }
	    print " filter $l\n" if $VERBOSE > 1;
	    push(@filts, qr/$l/);
	}
	close($if);
    }
    return \@filts;
}

sub r_pystr
{
    my $fd = shift;
    my ($len,$str)=('','');
    my ($c,$rd,$b) = (4,0,'');
    while ($c > 0) {
	$rd = sysread($fd,$b,$c);
	warn("failed to read len: $!"), return undef if !defined($rd);
	warn("not enough data for len"), return undef if !$rd;
	$len .= $b;
	$c -= $rd;
    }
    $len = unpack('V',$len);
    while ($len > 0) {
	$rd = sysread($fd,$b,$len);
	warn("failed to read data: $!"), return undef if !defined($rd);
	warn("not enough data"), return undef if !$rd;
	$str .= $b;
	$len -= $rd;
    }
    return $str;
}

sub read_pydict_entry
{
    my $f = shift;
    my ($buf,$rd);
    FIL: while (1) {
	# object type identifier
	$rd = sysread($f, $buf, 1);
	last FIL if $rd == 0;
	warn("p4: object type: $!\n"),last if $rd != 1;
	# '{' is a python marshalled dict
	warn("p4: object type: not {\n"),last if $buf ne '{';
	my $ent = {};
	PAIR: while (1) {
	    my ($b,$key);
	    # key type identifier
	    $rd = sysread($f, $b, 1);
	    warn("p4: key type: $!\n"),last FIL if $rd != 1;
	    if ($b eq 's') { # length-prefixed string
		$key = r_pystr($f);
		warn("p4: key: $!\n"),last FIL if !defined($b);
	    } elsif ($b eq '0') { # NULL-element, end of entry
		last PAIR;
	    } else {
		die("p4: key type: not s (string)\n");
		last FIL;
	    }
	    # value type identifier
	    $rd = sysread($f, $b, 1);
	    warn("p4: $key value type: $!\n"),last FIL if $rd != 1;
	    if ($b eq 's') { # length-prefixed string
		$b = r_pystr($f);
		warn("p4: $key value: $!\n"),last FIL if !defined($b);
		$ent->{$key} = $b;
	    } elsif ($b eq 'i') { # 4-byte integer
		$rd = sysread($f, $b, 4);
		warn("p4: $key value data: $!\n"),last FIL if $rd != 4;
		$ent->{$key} = unpack('V',$b);
	    } else {
		warn("p4: $key value type: not s ($b)\n");
		last FIL;
	    }
	}
	return $ent;
    }
    return undef;
}

sub p4user_to_env {
    my $u = shift;
    $ENV{GIT_AUTHOR_NAME}  = '';
    $ENV{GIT_AUTHOR_EMAIL} = '';
    return if !defined($u);
    if (!exists($P4USERS{$u})) {
	my ($mail,$name) = grep {/^(Email|FullName):/} qx{p4 user -o $u};
	if ($? == 0 and defined($mail) and defined($name)) {
	    s/^\S+:	([^\r\n]*)\r?\n$/$1/so for ($mail,$name);
	    if (length($name) and length($mail)) {
		$P4USERS{$u} = {name=>$name, email=>$mail};
	    }
	}
    }
    if ($P4USERS{$u}) {
	$p4u = $P4USERS{$u};
	$ENV{GIT_AUTHOR_NAME}  = $p4u->{name};
	$ENV{GIT_AUTHOR_EMAIL} = $p4u->{email};
    }
    return 1;
}

sub p4_get_change {
    my ($fd,$p4);
    my $cl = shift;
    if (!open($fd, '>', "$GIT_DIR/p4/files")) {
	warn "p4/files: $!\n";
	return;
    }
    print $fd "-o\n$cl\n";
    close($fd);
    if (!open($p4, "p4 -x $GIT_DIR/p4/files change|")) {
	warn "p4: failed to read p4 change $cl: $!\n";
	return;
    }
    my @change = <$p4>;
    close($p4);
    return @change;
}

sub cl2msg {
    my $cl = shift;
    my($o1,$o2,$i);
    if(!open($o1, '>>', "$GIT_DIR/p4/msg")) {
	warn "p4/msg: $!\n";
	return;
    }
    binmode($o1);
    if(!open($o2, '>>', "$GIT_DIR/p4/p4msg")) {
	warn "p4/p4msg: $!\n";
	close($o1);
	return
    }
    binmode($o2);
    if(!open($i, '-|', "p4 describe -s $cl")){
	warn "p4 describe: $!\n";
	close($o1);
	close($o2);
	return
    }
    binmode($i);
    print $o1 "$cl: " if $FULL_DESC;
    print $o2 "$cl: ";
    my @a;
    my $u = undef;
    while (my $l = <$i>) {
	if ($l =~ /^Change \d+ by (\S+)@[^ ]* on ([^\r\n]*)/so) {
	    $u = $1;
	    $ENV{GIT_AUTHOR_DATE} = $2 if length($2);
	}
	last if $FULL_DESC < 2 and $l =~ /^\s*Affected files \.{3}\s*$/so;
	$l =~ s/\r?\n$//so;
	push @a, $l;
    }
    close($i);
    print $o2 substr($a[2],1),"\n"; # p4 side-branch commit description
    close($o2);
    # import branch commit description
    if ($FULL_DESC > 1) {
	# desc level 2+: keep the Change line
	print $o1 map {"$_\n"} (substr($a[2],1),"\n",@a);
    } else {
	# levels 0 and 1: remove the Change line
	print $o1 map { (length($_) ? substr($_,1):'')."\n" } @a[2..$#a];
    }
    close($o1);
    p4user_to_env($u);
}

# looks for p4import/ commit which points to the given reference
# returns undef if not found
sub git_find_p4info {
    my $branch = shift;
    if (!%P4IMPORT_HEADS) {
	foreach my $l (qx{git show-ref}) {
	    $P4IMPORT_HEADS{$2} = $1
		if $l =~ m!^([0-9a-f]{40}) refs/(p4import/[^\r\n]+)\r?\n!so;
	}
    }
    my ($commit,$parent,$p4commit,$p4parent);
    my $r = git_rev_parse($branch);
    return undef if !defined($r);
    while (my ($k,$p4head) = each %P4IMPORT_HEADS) {
	my $commit;
	do {
	    print "trying $k:$p4head\n" if $VERBOSE >3;
	    ($commit,$p4head) =
		grep { s/^parent ([0-9a-f]{40}).*/$1/s }
		qx{git cat-file commit $p4head};
	    $commit = $p4head = '' if $?;
	    return $p4head if $commit eq $r;
	} while (defined($p4head) && length($p4head));
    }
    warn "$branch is not imported from Perforce\n";
    return undef;
}

sub git_get_p4have {
    my $p4head = shift;
    my $p4have = undef;
    if (defined($p4head) and length($p4head) and
	open(my $f, '-|', "git cat-file blob $p4head:have")) {
	my $old = $/;
	$/ = "\0";
	my $cnt = 0;
	while(1) {
	    my $p4name = <$f>;
	    last if !defined($p4name);
	    $p4name =~ s/^.//so if $cnt; # remove \n
	    my $name = <$f>;
	    my $rev = <$f>;
	    last if !defined($name) or !defined($rev);
	    chop($p4name,$name,$rev);
	    ++$cnt;
	    if (defined($p4have)) {
		$p4have->{$p4name} = [$name,$rev];
	    } else {
		$p4have = {$p4name=>[$name,$rev]};
	    }
	}
	$/ = $old;
	close($f);
	print "loaded $cnt revisions from $p4head\n" if $VERBOSE;
    }
    return $p4have;
}

sub p4_get_have {
    print "reading state of $P4CLIENT\n" if $VERBOSE;
    my ($p4have, $fdi);
    open($fdi, "p4 -G -c $P4CLIENT -H $P4HOST -d $P4ROOT have|") or
	die "p4 have: $!\n";
    binmode($fdi);
    my ($err,$ent) = (0,undef);
    while (defined($ent=read_pydict_entry($fdi))) {
	if (defined($ent->{code}) and defined($ent->{data})) {
	    ++$err if $ent->{code} eq 'error';
	    print STDERR "p4: $ent->{code}: $ent->{data}\n";
	    next;
	}
	next if !defined($ent->{depotFile});
	next if !defined($ent->{clientFile});
	if (defined($p4have)) {
	    $p4have->{$ent->{depotFile}}=[$ent->{clientFile},$ent->{haveRev}];
	} else {
	    $p4have={$$ent->{depotFile}=>[$ent->{clientFile},$ent->{haveRev}]};
	}
    }
    close($fdi);
    return $p4have;
}

sub git_p4_init {
    my ($commit,$parent,$p4commit,$p4parent);
    my $HEAD = git_rev_parse('HEAD');
    $HEAD = '' if !defined($HEAD);
    my $p4head = git_rev_parse("refs/p4import/$P4CLIENT");
    $p4head = '' if !defined($p4head);
    die "No HEAD commit! Refusing to import.\n" if !length($HEAD);
    if (length($p4head)) {
	($commit,$p4parent) =
	    grep { s/^parent (.{40}).*/$1/s }
	    qx{git cat-file commit $p4head};
	$commit = $p4parent = '' if $?;
	$p4parent = '' if !defined($p4parent);
    } else {
	$commit = $p4parent = '';
    }
    while (($commit ne $HEAD) and length($p4parent)) {
	$p4head = $p4parent;
	($commit,$p4parent) =
	    grep { s/^parent (.{40}).*/$1/so }
	    qx{git cat-file commit $p4head};
	$commit = $p4parent = '' if $?;
	$p4parent = '' if !defined($p4parent);
	if ($VERBOSE and ($HEAD eq $commit)) {
	    print "found p4 import commit ";
	    system('git','name-rev',$p4head);
	}
    }
    if ($HEAD ne $commit) {
	$HEAD_FROM_P4 = 0;
	warn "Current HEAD is not from $P4CLIENT, doing full import\n";
    } else {
	$HEAD_FROM_P4 = 1;
    }
    my $p4have = undef;
    if (!$FULL_IMPORT and ($HEAD eq $commit) and length($p4head)) {
	if (open(my $f, '-|', "git cat-file blob $p4head:have")) {
	    my $old = $/;
	    $/ = "\0";
	    my $cnt = 0;
	    while(1) {
		my $p4name = <$f>;
		last if !defined($p4name);
		$p4name =~ s/^.//so if $cnt; # remove \n
		my $name = <$f>;
		my $rev = <$f>;
		last if !defined($name) or !defined($rev);
		chop($p4name,$name,$rev);
		++$cnt;
		if (defined($p4have)) {
		    $p4have->{$p4name} = [$name,$rev];
		} else {
		    $p4have = {$p4name=>[$name,$rev]};
		}
	    }
	    $/ = $old;
	    close($f);
	    print "loaded $cnt revisions from $p4head\n" if $VERBOSE;
	}
    }
    return ($HEAD, $p4head, $p4have);
}

sub get_one_line {
    my ($line) = qx{@_};
    return undef if $?;
    $line = '' if !defined($line);
    $line =~ s/\r?\n//gs;
    return $line;
}

sub git_rev_parse {
    return get_one_line('git', 'rev-parse', shift);
}

sub git_write_tree {
    my $sha1 = get_one_line('git','write-tree');
    return undef if !defined($sha1) or !length($sha1);
    return $sha1;
}

sub git_commit_tree {
    my $sha1 = get_one_line('git','commit-tree',@_);
    return undef if !defined($sha1) or !length($sha1);
    return $sha1;
}

sub git_hash_stdin {
    my $sha1 = get_one_line('git','hash-object','-t','blob','-w','--stdin');
    return undef if !defined($sha1) or !length($sha1);
    return $sha1;
}

sub git_hash_file {
    open(STDIN, '<', $_[0]) or die "$0: git_hash_file $_[0]: $!\n";
    return git_hash_stdin();
}

sub git_update_ref_directly {
    return system('git','update-ref',@_);
}
sub git_update_ref {
    my ($msg,$refname,$refval) = @_;
    if ($refname =~ m!^(ORIG_|FETCH_|MERGE_)?HEAD$!o) {}
    elsif ($refname =~ s!^/+!!o) {}
    elsif ($refname =~ m!^refs/!o) {}
    elsif ($refname =~ m!^(heads|remotes|tags|p4import)/!o) {
	$refname = "refs/$refname"
    } else { $refname = "refs/heads/$refname" }
    print STDERR "Updating $refname with $refval\n" if $VERBOSE > 1;
    return git_update_ref_directly('-m',$msg,$refname,$refval);
}

sub git_p4_commit {
    my ($HEAD, $p4head) = @_;
    my ($commit,$parent,$p4commit,$p4parent);

    my ($fdo,$fdi,$rc);
    $rc = system('git','diff-index','--exit-code','--quiet','--cached','HEAD');
    if ($rc == 0) {
	warn("No changes\n");
	return;
    }

    return if $DRYRUN;

    if (!@DESC && !$EDIT_COMMIT) {
	warn "$0: no commit description given\n";
	return;
    }

    my $p4x = "$GIT_DIR/p4/idx.tmp";
    unlink($p4x);

    $ENV{PAGER} = 'cat';

    if (!defined($SPEC) or !open(STDIN, '<', $SPEC)) {
	if ( $^O eq 'MSWin32' ) {
	    open(STDIN, '<', 'NUL') or die "$SPEC: $!\n";
	} else {
	    open(STDIN, '<', '/dev/null') or die "$SPEC: $!\n";
	}
    }
    my $p4spec = git_hash_stdin();
    die "Failed to store $SPEC in git repo\n" if !defined($p4spec);

    my $p4clnt = git_hash_file("$GIT_DIR/p4/client.def");
    die "Failed to save mappings of $P4CLIENT in git repo" if !defined($p4clnt);

    if (!defined($P4HAVE_FILE)) {
	print "reading state of $P4CLIENT\n" if $VERBOSE;
	$P4HAVE_FILE = "$GIT_DIR/p4/have";
	open($fdo, '>', $P4HAVE_FILE) or die "p4/have: $!\n";
	binmode($fdo);
	open($fdi, "p4 -G -c $P4CLIENT -H $P4HOST -d $P4ROOT have|") or
	    die "p4 have: $!\n";
	binmode($fdi);
	my ($cnt,$err,$ent) = (0,0,undef);
	while (defined($ent=read_pydict_entry($fdi))) {
	    if (defined($ent->{code}) and defined($ent->{data})) {
		++$err if $ent->{code} eq 'error';
		print STDERR 'p4: '.$ent->{code}.': '.$ent->{data}."\n";
		next;
	    }
	    next if !defined($ent->{depotFile});
	    next if !defined($ent->{clientFile});
	    ++$cnt;
	    print $fdo "$ent->{depotFile}\0",
		       "$ent->{clientFile}\0",
		       "$ent->{haveRev}\0\n";
	}
	close($fdi);
	close($fdo);
	exit 1 if $err; # the error already reported
	die "The client $P4CLIENT has nothing\n" if !$cnt;
    }

    my $p4have = git_hash_file($P4HAVE_FILE);
    die "Failed to save state of $P4CLIENT in git repo" if !defined($p4have);

    #
    # Prepare commit messages
    #
    unlink("$GIT_DIR/p4/msg", "$GIT_DIR/p4/p4msg");
    open($fdo, '>', "$GIT_DIR/p4/msg"); close($fdo);
    open($fdo, '>', "$GIT_DIR/p4/p4msg"); close($fdo);

    foreach my $i (@DESC) {
	$i =~ s/^(.)//o;
	if ('c' eq $1) {
	    print "reading changes for $i\n" if $VERBOSE;
	    cl2msg($i);
	} elsif ('f' eq $1) {
	    my($o1,$o2,$i);
	    if (open($o1, '>>', "$GIT_DIR/p4/msg")) {
		if (open($o2, '>>', "$GIT_DIR/p4/p4msg")) {
		    if (open($i, '<', $i)) {
			my $n = 0;
			while(<$i>) {
			    $n++;
			    print $o1 $_;
			    print $o2 $_ if $n == 1;
			}
			close($i);
		    }
		    close($o2);
		}
		close($o1);
	    }
	} elsif ('4' eq $1) {
	    print "reading changes for $i\n" if $VERBOSE;
	    my $change = get_one_line('p4', 'changes', '-m1', $i);
	    if (!defined($change) or $change !~ m/\s+(\d+)\s/) {
		die "$i does not resolve into a change number\n";
	    }
	    cl2msg($1);
	}
    }
    system("$editor $GIT_DIR/p4/msg") if $EDIT_COMMIT;

    # copy mirror-branch commit message into side-branch
    # commit message if no other description were given.
    if (!-s "$GIT_DIR/p4/p4msg") {
	open($fdi, '<', "$GIT_DIR/p4/msg") or die "$GIT_DIR/p4/msg: $!\n";
	sysread($fdi,$buf,-s "$GIT_DIR/p4/msg");
	close($fdi);
	open($fdo, '>>', "$GIT_DIR/p4/p4msg") or die "$GIT_DIR/p4/p4msg: $!\n";
	syswrite($fdo,$buf);
	close($fdo);
    }

    #
    # Store the imported file data
    #

    if ($VERBOSE < 2) {
	if ( $^O eq 'MSWin32' ) { open(STDERR, "NUL") }
	else { open(STDERR, "/dev/null") }
    }

    my $remove_merge_heads = 0;
    my $tree = git_write_tree();
    die "failed to write current tree\n" if !defined($tree);
    open(STDIN, '<', "$GIT_DIR/p4/msg") or die "p4/msg: $!\n";
    if (length($HEAD)) {
	my @mergeparents;
	if (open($fd, '<', "$GIT_DIR/MERGE_HEAD")) {
	    while(<$fd>) {
		s/\r?\n$//gs;
		push(@mergeparents, '-p', $_) if /^[0-9a-f]{40}$/;
	    }
	    close($fd);
	}
	$commit = git_commit_tree($tree, '-p', $HEAD, @mergeparents);
	die "failed to commit the merged tree\n" if !defined($commit);
	$remove_merge_heads = 1 if @mergeparents;
    } else {
	$commit = git_commit_tree($tree);
	die "failed to commit current tree\n" if !defined($commit);
    }
    print "current tree stored in commit $commit\n" if $VERBOSE;

    #
    # Storing import control data
    #
    $ENV{GIT_INDEX_FILE} = $p4x;
    open($fdo, '|-', 'git update-index --add --index-info') or
	die "could not start git update-index\n";
    binmode($fdo);
    print $fdo "100644 $p4spec\tspec\n";
    print $fdo "100644 $p4clnt\tclient\n";
    print $fdo "100644 $p4have\thave\n";
    close($fdo);
    if($?) {
	die "Failed to store $SPEC in p4import index and git repo\n".
	    "Failed to save mappings of $P4CLIENT in p4import index and git repo\n".
	    "Failed to save state of $P4CLIENT in p4import index and git repo\n"
    }
    my $p4tree = git_write_tree();
    die "Failed to store $SPEC (tree) in git repo\n" if $?;

    # Bind import control data to the file data
    open(STDIN, '<', "$GIT_DIR/p4/p4msg") or die "p4/p4msg: $!\n";
    $p4commit = length($p4head) ?
	git_commit_tree($p4tree, '-p', $commit, '-p', $p4head):
	git_commit_tree($p4tree, '-p', $commit);
    die "Failed to store $SPEC (commit) in git repo\n" if $?;

    # Finishing touches: update references
    if (!$DRYRUN) {
	git_update_ref('backup ref of current branch','/p4/backup-HEAD','HEAD');
	git_update_ref('backup ref of p4import',
		       '/p4/backup-p4import',"refs/p4import/$P4CLIENT");
	$rc = git_update_ref('data of p4import','HEAD',$commit);
	die "Failed to update HEAD\n" if $rc;
	unlink("$GIT_DIR/MERGE_HEAD") if $remove_merge_heads;
	$rc = git_update_ref('p4import',"p4import/$P4CLIENT",$p4commit);
	die "Failed to store $SPEC (reference) in git repo\n" if $rc;
    }

    $ENV{GIT_PAGER} = 'cat';
    if ($VERBOSE) {
	print STDOUT (grep {s/\r?\n//gs;s/.*?\s//} qx{git name-rev refs/p4import/$P4CLIENT}), ":\n";
	system('git','log','--max-count=1','--pretty=format:%h %s%n',$p4commit);
    }
    print STDOUT (grep {s/\r?\n//gs;s/.*?\s//} qx{git name-rev HEAD}),":\n";
    system('git','log','--max-count=1','--pretty=format:%h %s%n',$commit);
}

sub import_p4_dir {
    my ($p4path, $local_path, $revrange, $parent, $branch) = @_;
    my ($fd, $ent, $error, $rc) = (undef,undef,0,0);

    print "Running changes\n" if $VERBOSE;

    if (open($fd, '>', "$GIT_DIR/p4/files")) {
	print "looking for changes $p4path/...$revrange\n" if $VERBOSE > 1;
	print $fd "$p4path/...$revrange\n";
	close($fd);
    } else {
	die "$0: p4/files: $!\n"
    }
    open(STDIN, '<', "$GIT_DIR/p4/files") or die "$0: p4/files: $!\n";
    die "$0: changes $p4path: $!\n" if !open($fd, '-|', 'p4 -G -x - changes');
    my %CHANGES = ();
    while (defined($ent = read_pydict_entry($fd))) {
	next if $error;
	if ($ent->{code} eq 'error') {
	    warn "$0: p4: $ent->{data}\n";
	    $error = 1;
	    next;
	}
	print "change $ent->{change}\n" if $VERBOSE > 1;
	$CHANGES{$ent->{change}} = {
	    change =>$ent->{change},
	    desc   =>'', # have to read the desc anyway with describe
	    mtime  =>$ent->{'time'},
	    user   =>$ent->{user},
	    files  =>[],
	};
    }
    close($fd);
    warn("$0: nothing found\n"), exit(0) if !%CHANGES;
    exit 1 if $error;

    print "Running describe for ".scalar(keys %CHANGES)." change lists\n"
	if $VERBOSE;

    if (open($fd, '>', "$GIT_DIR/p4/files")) {
	print $fd map { "$_\n"} sort {$a <=> $b} keys %CHANGES;
	close($fd);
    } else {
	die "$0: p4/files: $!\n"
    }
    open(STDIN, '<', "$GIT_DIR/p4/files") or die "$0: p4/files: $!\n";
    die "$0: describe: $!\n" if !open($fd, '-|', 'p4 -G -x - describe');
    while (defined($ent = read_pydict_entry($fd))) {
	next if $error;
	if ($ent->{code} eq 'error') {
	    warn "$0: p4: $ent->{data}\n";
	    $error = 1;
	    next;
	}
	$CHANGES{$ent->{change}}->{desc} = $ent->{desc};
	$CHANGES{$ent->{change}}->{files} = {};
	for (my $i=0;; ++$i) {
	    my $fn = $ent->{"depotFile$i"};
	    last if !defined($fn);
	    next if $fn !~ m!^$p4path(/|$)!;
	    $CHANGES{$ent->{change}}->{files}->{$fn} = {
		action => $ent->{"action$i"},
	    };
	    # $ent->{"type$i"} : text, binary
	}
    }
    close($fd);
    exit 1 if $error;

    print "Reading file data and creating git history\n" if $VERBOSE;

    # Prepare clean index under the local path
    $ENV{GIT_INDEX_FILE} = "$GIT_DIR/p4/idx.tmp";
    unlink($ENV{GIT_INDEX_FILE});
    if (length($parent)) {
	$rc = system('git', 'read-tree', '-i', '--reset', $parent);
	die "git read-tree $parent\n" if $rc;
	$rc = system('git','update-index','--force-remove','--',$local_path);
	die "git update-index $local_path\n" if $rc;
    }
    # Read file data for each change list
    my $first = undef;
    my $ch;
    foreach my $k (sort {$a <=> $b} keys %CHANGES) {
	$ch = $CHANGES{$k};
	print "$k\n" if $VERBOSE > 1;
	# Read the full tree for the first change number
	if (open($fd, '>', "$GIT_DIR/p4/files")) {
	    if (!defined($first)) {
		print $fd "$p4path/...\@$k\n";
		$first = $k;
	    } else {
		foreach my $f (keys(%{$ch->{files}})) {
		    print $fd "$f\@$k\n";
		}
	    }
	    close($fd);
	} else {
	    die "$0: p4/files: $!\n"
	}
	open(STDIN, '<', "$GIT_DIR/p4/files") or die "$0: p4/files: $!\n";

	print "Reading file data for $k\n" if $VERBOSE > 1;

	die "$0: print: $!\n" if !open($fd, '-|', 'p4 -G -x - print');
	my $tmpfile = "$GIT_DIR/p4/cp.tmp";
	while (defined($ent = read_pydict_entry($fd))) {
	    next if $error;
	    if ($ent->{code} eq 'error') {
		warn "$0: p4: $ent->{data}\n";
		$error = 1;
		next;
	    }
	    if ($ent->{code} eq 'binary') {
		if (!defined($f)) {
		    warn "$0: file data without stat info\n";
		    $error = 1;
		    next;
		}
		$f->{size} += length($ent->{data});
		if (length($ent->{data})) {
		    if (!defined(syswrite($f->{tmpfd}, $ent->{data}))) {
			warn "$f->{depotFile}: $tmpfile: $!\n";
			close($f->{tmpfd});
			$f = undef;
			$error = 1;
			next;
		    }
		} else {
		    # FILE FINISHED IF AN EMPTY BINARY PACKET RECEIVED
		    close($f->{tmpfd});
		    my $fn = $f->{depotFile};
		    # put file data into git repo
		    my $tmpsha1 = git_hash_file($tmpfile);
		    die "Failed to save $fn in git repo\n" if !defined($tmpsha1);
		    unlink($tmpfile);
		    $f->{sha1} = $tmpsha1;

		    print "$k\t$tmpsha1\t$f->{size}\t$fn\n" if $VERBOSE > 2;
		    $ch->{files}->{$fn} = $f;

		    delete $f->{depotFile}; # cleanup
		    delete $f->{tmpfd}; # cleanup
		    $f = undef;
		}
		next;
	    }
	    if ($ent->{code} eq 'stat') {
		die "$0: file $f->{depotFile} truncated\n" if defined($f);
		die "$ent->{depotFile}: the leading path not expected\n"
		    if substr($ent->{depotFile},0,length($p4path)) ne $p4path;
		die "$ent->{depotFile}\@$ent->{change}: the change not expected\n"
		    if $ent->{change} > $k;

		my $prev = $ch->{files}->{$ent->{depotFile}};
		$f = {
		    action => defined($prev->{action}) ? $prev->{action}:'add',
		    change => $ent->{change},
		    depotFile => $ent->{depotFile},
		    mtime => $ent->{'time'},
		    size => 0,
		};
		my $ft;
		open($ft,'>',$tmpfile) or die "$tmpfile: $!\n";
		$f->{tmpfd} = $ft;
	    }
	}
	close($fd);
	exit 1 if $error;
	my $modcnt = 0;
	my @delfiles = ();
	my $fdo = undef;
	open($fdo, '|-', 'git update-index -z --replace --add --index-info') or
	    die "could not start git update-index\n";
	binmode($fdo);
	while (my ($fn,$f) = each %{$ch->{files}}) {
	    if (substr($fn,0,length($p4path)) ne $p4path) {
		die "$fn: the leading path not expected\n";
	    }
	    my $locfile = $fn;
	    substr($locfile,0,length($p4path)) = $local_path;
	    $locfile =~ s!^/+!!o;
	    push(@delfiles, $locfile), next if $f->{action} eq 'delete';
	    print "\t$f->{sha1}\t$locfile\n" if $VERBOSE > 1;
	    my $mode = '100644';
	    $mode = '100755' if $locfile =~ /\.(exe|bat|cmd|pl|dll|so)$/io;
	    print $fdo "$mode $f->{sha1}\t$locfile\0";
	    ++$modcnt;
	}
	close($fdo);
	die "failed to build the trees in $local_path for $k\n" if $?;
	if (@delfiles) {
	    open($fdo, '|-', 'git update-index -z --force-remove --stdin') or
		die "could not start git update-index to remove files\n";
	    binmode($fdo);
	    print $fdo map { "$_\0" } @delfiles;
	    close($fdo);
	    print map { "\tdelete $_\n" } @delfiles if $VERBOSE > 1;
	    die "failed to clean the trees in $local_path for $k\n" if $?;
	    $modcnt += scalar @delfiles;
	}
	my $tree = git_write_tree();
	die "Failed to store tree of $k in git repo\n" if !defined($tree);
	print "\ttree of $k written as $tree\n" if $VERBOSE > 1;
	# prepare commit
	open($fdo,'>',"$GIT_DIR/p4/msg") or die "p4/msg: $!\n";
	print $fdo "$k: $ch->{desc}";
	close($fdo);
	p4user_to_env($ch->{user});
	$ENV{GIT_AUTHOR_DATE} = $ch->{mtime};
	open(STDIN, '<', "$GIT_DIR/p4/msg") or die "p4/msg (commit): $!\n";
	my $commit = length($parent) ?
	    git_commit_tree($tree, '-p', $parent): git_commit_tree($tree);
	die "Failed to commit $k in git repo\n" if !defined($commit);
	print "$k committed as $commit";
	print " ($modcnt modification".($modcnt==1?'':'s').')' if $VERBOSE;
	print "\n";
	$parent = $commit;
    }
    if (!$DRYRUN and length($parent)) {
	git_update_ref("backup ref of $branch", "/p4/backup-$branch",$branch)
	    if defined($branch);
	$branch = '/p4/IMPORT' if !defined($branch);
	my $rc = git_update_ref("p4 import $branch", $branch, $parent);
	die "failed to update $branch with $parent\n" if $rc;
	$branch =~ s!^/+!!;
	print "Branch '$branch' updated with state from $ch->{change}\n";
	$ENV{GIT_PAGER} = 'cat';
	system('git','log','--max-count=1','--pretty=format:%h %s%n',$branch);
    }
}

sub git_show_diffs {
    my $sep = $/;
    $/="\0";
    my ($show, $cnt) = (0, 0);
    if (open(F, '-|', 'git diff-files -r --name-only -z')) {
	while (<F>) {
	    my $c = chop;
	    $_ .= $c if $c ne "\0";
	    print "Changed files:\n" if !$show;
	    print " $_\n";
	    $show = 1;
	    $cnt++;
	}
	close(F);
    }
    if (open(F, '-|', 'git diff-index --cached -r -z HEAD')) {
	$show = 0;
	my ($diff, $info) = (0, 1);
	while (<F>) {
	    my $c = chop;
	    $_ .= $c if $c ne "\0";
	    if ($info) {
		next if !/^:(\d{6}) (\d{6}) ([0-9a-f]{40}) ([0-9a-f]{40}) ./o;
		# show only content changes, p4 does not support exec-bit anyway
		$diff = $3 ne $4;
	    } elsif ($diff) {
		print "Changes between index and HEAD:\n" if !$show;
		print " $_\n";
		$show = 1;
		$cnt++;
	    }
	    $info = !$info;
	}
	close(F);
    }
    $/ = $sep;
    return $cnt;
}

sub git_sync_to {
    my $rc;
    my $branch = shift;
    if (!$FORCE) {
	$rc = system('git', 'diff', '--quiet');
	warn("There are changes in the working directory. Refusing to sync\n")
	    if $rc;
	$rc = system('git', 'diff', '--quiet', '--cached');
	warn("There are changes in the index. Refusing to sync\n") if $rc;
    }
    my $p4head = git_find_p4info($branch);
    print "found p4 import commit $p4head for $branch\n"
	if ($VERBOSE > 1 or $DRYRUN) and defined $p4head;
    return 0 if $DRYRUN and defined $p4head;
    my $p4have = git_get_p4have($p4head);
    die "$p4head has no p4 data\n" if !defined($p4have);
    my ($fd,$fdo);
    die "reading client $P4CLIENT: $!\n"
	if !open($fd, '-|', "p4 -G client -o $P4CLIENT");
    binmode($fd);

    my %cdata = (Client=>'', Description=>'', LineEnd=>'', Options=>'');

    while (defined($ent = read_pydict_entry($fd))) {
	next if $error;
	if ($ent->{code} eq 'error') {
	    warn "p4 client $P4CLIENT: $ent->{data}\n";
	    $error = 1;
	    next;
	}
	foreach my $k (keys %cdata) {
	    $cdata{$k} = $ent->{$k} if defined $ent->{$k};
	}
    }
    close($fd);
    die "client.def: $!\n" if !open($fdo, '>', "$GIT_DIR/p4/client.def");
    binmode($fdo);
    print $fdo "Client: $P4CLIENT\n\n";
    print $fdo "Description:\n";
    $cdata{Description} =~ s/\n/\n\t/gs;
    $cdata{Description} =~ s/\t$//s;
    print $fdo "\t$cdata{Description}\n";
    my $wd = cwd();
    $wd =~ s/\\/\//go;
    print $fdo "Root:\t$wd\n\n";
    print $fdo "LineEnd:\t$cdata{LineEnd}\n\n";
    print $fdo "Options:\t$cdata{Options}\n\n";
    print $fdo "View:\n";
    print $fdo
	map {
	    my $fn = $p4have->{$_}->[0];
	    $fn =~ s!^//[^/]*/!//$P4CLIENT/!o;
	    s/([ \t"'@*#%])/sprintf("%%%02x",ord($1))/geo for ($_,$fn);
	    "\t$_ $fn\n"
	} sort { $a cmp $b }
	keys %{$p4have};
    close($fdo);
    print "loading client $P4CLIENT\n";
    open(STDIN, '<', "$GIT_DIR/p4/client.def") or die "client.def: $!\n";
    run_or_exit('p4', 'client', '-i');
    die "files: $!\n" if !open($fdo, '>', "$GIT_DIR/p4/files");
    print $fdo
	map {
	    my $fn = $_;
	    s/([ \t"'@*#%])/sprintf("%%%02x",ord($1))/geo;
	    "$_#$p4have->{$fn}->[1]\n"
	} sort { $a cmp $b }
	keys %{$p4have};
    close($fdo);
    print "syncing client $P4CLIENT\n";
    open(STDIN, '<', "$GIT_DIR/p4/files") or die "p4/files: $!\n";
    run_or_exit('p4', '-c', $P4CLIENT, '-H', $P4HOST, '-d', $P4ROOT,'-x', '-', 'sync');

    return defined($p4head) ? 0: 1;
}

sub git_p4_tag_HEAD {
    my $cl = shift;
    my @change = p4_get_change($cl);
    return 0x100 if !@change;
    my $d=0;
    my ($first_line) = grep {
	if ($d==1) {
	    if (!/^\s*[\r\n]*$/so) { $d=0; 1 }
	    else { 0 }
	} else {
	    $d=1 if /^Description:/o; 0
	}
    } @change;
    my $fd;
    if (!open($fd, '>', "$GIT_DIR/p4/desc")) {
	warn "p4/desc: $!\n";
	return 0x100;
    }
    unshift(@change, '');
    $first_line =~ s/^\s+//;
    unshift(@change, $first_line);
    print $fd map {s/\r?\n$//so;"$_\n"} @change;
    close($fd);
    my $rc = system('git', 'tag', '-f', '-F', "$GIT_DIR/p4/desc", "p4/$cl");
    print "HEAD tagged p4/$cl\n" if !$rc;
    return $rc;
}

sub git_p4_merge {
    my ($source_head,$do_merge,$auto_submit) = @_;
    my $rc = system('git', 'diff-files', '--quiet');
    exit(127) if $rc & 0xff; # error starting the program
    die "There are changes in $P4ROOT. Clean them up first.\n" if $rc;
    $source_head = 'HEAD' if !defined($source_head);
    my $mergehead = git_rev_parse($source_head);
    exit(1) if !defined($mergehead) or !length($mergehead);
    my $fast_forward = 1;
    # Check if the given reference is a direct descendant of current branch
    if ($source_head ne 'HEAD') {
	my $sha1 = get_one_line('git', 'rev-list', '--max-count=1', "$mergehead..HEAD");
	exit 1 if !defined($sha1);
	if (length($sha1) and defined($sha1) and $sha1 =~ /^[0-9a-f]{40}\b/) {
	    my $msg = "HEAD does not fast-forward to $source_head\n";
	    $do_merge ? warn("Warning: $msg"): die("Fatal: $msg");
	    $fast_forward = 0;
	}
    }
    print "Checking out $source_head ($mergehead) for p4 edit\n" if $VERBOSE;
    my $cnt;
    my @files = ();
    my $sep = $/;
    $/="\0";
    if (open(F, '-|', "git diff-index -R --cached -r -z $mergehead")) {
	my ($diff, $info, $M) = (0, 1, '');
	while (<F>) {
	    my $c = chop;
	    $_ .= $c if $c ne "\0";
	    if ($info) {
		next if !/^:\d{6} \d{6} ([0-9a-f]{40}) ([0-9a-f]{40}) (\w+)/o;
		# use only content changes, p4 does not support exec-bit
		$diff = $1 ne $2;
		$M = $3; # change type marker
	    } elsif ($diff) {
		print "$M $_\n" if $VERBOSE;
		die "File contains characters which p4 cannot support\n"
		    if /[\n@#%*]/s;
		push @files, "$M$_";
		$cnt++;
	    }
	    $info = !$info;
	}
	close(F);
    }
    $/ = $sep;
    if (!$cnt) {
	warn "$0: No content changes found between HEAD and $source_head";
	return 0;
    }
    # Create a new changelist
    my $p4;
    open($p4, "p4 -c $P4CLIENT -H $P4HOST -d $P4ROOT change -o|") or
	die "$0: failed to create changelist\n";
    my @desc = map {s/\r?\n$//so; $_} <$p4>;
    close($p4);

    my $desc_pos = 0;
    foreach (@desc) {
	++$desc_pos;
	last if /^Description:/o;
    }
    my $editfd;
    die "$GIT_DIR/p4/desc.txt: $!\n"
	if !open($editfd, '>', "$GIT_DIR/p4/desc.txt");
    my $range = "..$mergehead";
    # because I have no information about what I am merging with:
    # this is the case when a push modified HEAD.
    $range = "${mergehead}^..$mergehead" if $source_head eq 'HEAD';
    if (open(my $fd, '-|', "git log $range")) {
	while(<$fd>) {
	    # I believe it is not possible to save this information
	    # in Perforce. They are primitive
	    next if /^(commit |Author:|Date:)/;
	    s/\r?\n$//so;
	    next if !length($_); # header/message separator line
	    s/^\s{4}//o;
	    print $editfd "$_\n" if defined($editfd);
	}
	close($fd);
    }
    print $editfd map { /^\s?(.*)/o;"$1\n" } @desc[$desc_pos..$#desc]; 
    close($editfd);

    system("$editor $GIT_DIR/p4/desc.txt") if $EDIT_COMMIT;
    die "$GIT_DIR/p4/desc.txt: $!\n"
	if !open($editfd, '<', "$GIT_DIR/p4/desc.txt");
    my @tmpdesc = <$editfd>;
    die "No change description, not merging.\n"
	if !grep {!m/^(\s|[\r\n])*$/so} @tmpdesc;
    splice(@desc, $desc_pos, $#desc + 1 - $desc_pos,
	   map {s/\r?\n$//so;" $_"} @tmpdesc);
    close($editfd);

    open($p4, '>', "$GIT_DIR/p4/changelist") or
	die "$GIT_DIR/p4/changelist: $!\n";
    print $p4 map {"$_\n"} @desc;
    close($p4);

    return 0 if $DRYRUN;

    open(STDIN, '<', "$GIT_DIR/p4/changelist") or
	die "$GIT_DIR/p4/changelist: $!\n";
    open($p4, "p4 -c $P4CLIENT -H $P4HOST -d $P4ROOT change -i|") or
	die "$0: failed to create changelist\n";
    my ($newchange) = grep {s/^Change (\d+) created\b.*/$1/so} <$p4>;
    close($p4);
    print "Checking out P4 files in changelist $newchange\n" if $VERBOSE;
    sub runp4 {
	run_or_exit('p4','-c',$P4CLIENT,'-H',$P4HOST,'-d',$P4ROOT,@_);
    }
    # open files for edit
    $cnt = 0;
    open($p4, '>', "$GIT_DIR/p4/files") or die "$GIT_DIR/p4/files: $!\n";
    print $p4 "-c\n$newchange\n";
    print $p4 (map {++$cnt; substr($_,1)."\n"} grep {/^M/} @files);
    close($p4);
    runp4('-x',"$GIT_DIR/p4/files", 'edit') if $cnt;
    exit(1) if $?;

    $cnt = 0;
    open($p4, '>', "$GIT_DIR/p4/files") or die "$GIT_DIR/p4/files: $!\n";
    print $p4 "-c\n$newchange\n";
    print $p4 (map {++$cnt; substr($_,1)."\n"} grep {/^A/} @files);
    close($p4);
    runp4('-x',"$GIT_DIR/p4/files", 'add') if $cnt;
    exit(1) if $?;

    $cnt = 0;
    open($p4, '>', "$GIT_DIR/p4/files") or die "$GIT_DIR/p4/files: $!\n";
    print $p4 "-c\n$newchange\n";
    print $p4 (map {++$cnt; substr($_,1)."\n"} grep {/^D/} @files);
    close($p4);
    runp4('-x',"$GIT_DIR/p4/files", 'delete') if $cnt;
    exit(1) if $?;

    # p4 modifies working directory on checkout, stupid thing
    system('git', 'update-index', '--refresh');
    if ($do_merge) {
	run_or_exit('git', 'merge', '--no-commit', $mergehead);
    } else {
	run_or_exit('git', 'read-tree', '-m', '-u', $mergehead);
	print "The state of $source_head($mergehead) is checked out.\n";
    }
    if (!$auto_submit) {
	print "A p4 changelist $newchange is prepared.\n";
	print "To submit:\n\tp4 submit -c $newchange\n";
	print "To commit:\n\tgit-p4-import.bat -y -C SUBMITTED_CHANGE\n"
	    if !$fast_forward;
    } else {
	die "p4/files: $!\n" if !open($p4, '>', "$GIT_DIR/p4/files");
	print $p4 "-c\n$newchange\n";
	close($p4);
	my @res;
	my $subchange = undef;
	@res = qx{p4 -c $P4CLIENT -H $P4HOST -d $P4ROOT -x $GIT_DIR/p4/files submit};
	die "p4: failed to run p4 submit -c $newchange\n" if $?;
	foreach(@res) {
	    print;
	    if (/^\s*Change (\d+) submitted\..*/s) {
		$subchange = $1;
	    }
	    elsif (/^\s*Change (\d+) renamed change (\d+) and submitted.*/s) {
		$subchange = $2 if $1 eq $newchange;
	    }
	}
	die "p4 submit behaved unexpectedly\n".
	    "To tag the current HEAD use \n".
	    "\tgit-p4-import --p4-tag <the new change>\n"
	    if !defined($subchange);
	$subchange =~ s/\r?\n$//so;
	print "Submitted as $subchange\n";
	if ($fast_forward) {
	    $rc = git_p4_tag_HEAD($subchange);
	    exit(127) if $rc & 0xff;
	    exit(1) if $rc;
	    print "Tagged as p4/$subchange\n";
	} else {
	    print "The merged tree is left in $P4ROOT.\n";
	    print "To commit:\n\tgit-p4-import.bat -y -C $subchange\n";
	}
    }
    return 0;
}

__END__
:__end_of_file__


^ permalink raw reply

* Draft release notes for v1.5.3, as of -rc1
From: Junio C Hamano @ 2007-07-13  6:40 UTC (permalink / raw)
  To: git
In-Reply-To: <7vzm20q1l7.fsf_-_@assigned-by-dhcp.cox.net>

GIT v1.5.3 Release Notes (draft)
========================

Updates since v1.5.2
--------------------

* The commit walkers other than http are officially deprecated,
  but still supported for now.

* The submodule support has Porcelain layer.

* There are a handful pack-objects changes to help you cope better
  with repositories with pathologically large blobs in them.

* For people who need to import from Perforce, a front-end for
  fast-import is in contrib/fast-import/.

* Comes with git-gui 0.8.0.

* Comes with updated gitk.

* New commands and options.

  - The hunk header output from "git diff" family can be customized
    with the attributes mechanism.  See gitattributes(5) for details.

  - "git stash" allows you to quickly save away your work in
    progress and replay it later on an updated state.

  - "git rebase" learned an "interactive" mode that let you
    pick and reorder which commits to rebuild.

  - "git fsck" can save its findings in $GIT_DIR/lost-found,
    without a separate invocation of "git lost-found" command.

  - $GIT_WORK_TREE environment variable can be used together with
    $GIT_DIR to work in a subdirectory of a working tree that is
    not located at "$GIT_DIR/..".

  - "git log" learned a new option "--follow", to follow
    renaming history of a single file.

  - "git-filter-branch" lets you rewrite the revision history of
    the current branch, creating a new branch. You can specify a
    number of filters to modify the commits, files and trees.

  - "git-cvsserver" learned new options (--base-path, --export-all,
    --strict-paths) inspired by git-daemon.

  - "git-submodule" command helps you manage the projects from
    the superproject that contain them.

  - In addition to core.compression configuration option,
    core.loosecompression and pack.compression options can
    independently tweak zlib compression levels used for loose
    and packed objects.

  - "git-ls-tree -l" shows size of blobs pointed at by the
    tree entries, similar to "/bin/ls -l".

  - "git-rev-list" learned --regexp-ignore-case and
    --extended-regexp options to tweak its matching logic used
    for --grep fitering.

  - "git-describe --contains" is a handier way to call more
    obscure command "git-name-rev --tags".

  - "git gc --aggressive" tells the command to spend more cycles
    to optimize the repository harder.

  - "git repack" can be told to split resulting packs to avoid
    exceeding limit specified with "--max-pack-size".

  - "git fsck" gained --verbose option.  This is really really
    verbose but it might help you identify exact commit that is
    corrupt in your repository.

  - "git format-patch" learned --numbered-files option.  This
    may be useful for MH users.

  - "git format-patch" learned format.subjectprefix configuration
    variable, which serves the same purpose as "--subject-prefix"
    option.

  - "git tag -n -l" shows tag annotations while listing tags.

  - "git cvsimport" can optionally use the separate-remote layout.

  - "git blame" can be told to see through commits that change
    whitespaces and indentation levels with "-w" option.

  - "git send-email" can be told not to thread the messages when
    sending out more than one patches.

  - "git config" learned NUL terminated output format via -z to
    help scripts.

  - "git init -q" makes the command quieter.

* Updated behavior of existing commands.

  - "git svn dcommit" retains local merge information.

  - "git config" to set values also honors type flags like --bool
    and --int.

  - core.quotepath configuration can be used to make textual git
    output to emit most of the characters in the path literally.

  - "git mergetool" chooses its backend more wisely, taking
    notice of its environment such as use of X, Gnome/KDE, etc.

  - "gitweb" shows merge commits a lot nicer than before.  The
    default view uses more compact --cc format, while the UI
    allows to choose normal diff with any parent.

  - snapshot files "gitweb" creates from a repository at
    $path/$project/.git are more useful.  We use $project part
    in the filename, which we used to discard.

  - "git cvsimport" creates lightweight tags; there is no
    interesting information we can record in an annotated tag,
    and the handcrafted ones the old code created was not
    properly formed anyway.

  - "git-push" pretends that you immediately fetched back from
    the remote by updating corresponding remote tracking
    branches if you have any.

  - The diffstat given after a merge (or a pull) honors the
    color.diff configuration.

  - "git-apply --whitespace=strip" removes blank lines added at
    the end of the file.

  - "git-fetch" over git native protocols with "-v" option shows
    connection status, and the IP address of the other end, to
    help diagnosing problems.

  - We used to have core.legacyheaders configuration, when
    set to false, allowed git to write loose objects in a format
    that mimicks the format used by objects stored in packs.  It
    turns out that this was not so useful.  Although we will
    continue to read objects written in that format, we do not
    honor that configuration anymore and create loose objects in
    the legacy/traditional format.

  - "--find-copies-harder" option to diff family can now be
    spelled as "-C -C" for brevity.

  - "git-mailsplit" (hence "git-am") can read from Maildir
    formatted mailboxes.

  - "git-cvsserver" does not barf upon seeing "cvs login"
    request.

  - "pack-objects" honors "delta" attribute set in
    .gitattributes.  It does not attempt to deltify blobs that
    come from paths with delta attribute set to false.

  - "new-workdir" script (in contrib) can now be used with a
    bare repository.

  - "git-mergetool" learned to use gvimdiff.

  - "gitview" (in contrib) has a better blame interface.

  - "git log" and friends did not handle a commit log message
    that is larger than 16kB; they do now.

  - "--pretty=oneline" output format for "git log" and friends
    deals with "malformed" commit log messages that have more
    than one lines in the first paragraph better.  We used to
    show the first line, cutting the title at mid-sentence; we
    concatenate them into a single line and treat the result as
    "oneline".

* Builds

  - old-style function definitions (most notably, a function
    without parameter defined with "func()", not "func(void)")
    have been eradicated.

* Performance Tweaks

  - git-pack-objects avoids re-deltification cost by caching
    small enough delta results it creates while looking for the
    best delta candidates.

  - git-pack-objects learned a new heuristcs to prefer delta
    that is shallower in depth over the smallest delta
    possible.  This improves both overall packfile access
    performance and packfile density.

  - diff-delta code that is used for packing has been improved
    to work better on big files.

  - when there are more than one pack files in the repository,
    the runtime used to try finding an object always from the
    newest packfile; it now tries the same packfile as we found
    the object requested the last time, which exploits the
    locality of references.

  - verifying pack contents done by "git fsck --full" got boost
    by carefully choosing the order to verify objects in them.


Fixes since v1.5.2
------------------

All of the fixes in v1.5.2 maintenance series are included in
this release, unless otherwise noted.

* Bugfixes

  - "gitweb" had trouble handling non UTF-8 text with older
    Encode.pm Perl module.

^ permalink raw reply

* Re: [PATCH] make git-send-email.perl handle email addresses with no names when Email::Valid is present
From: Greg KH @ 2007-07-13  6:34 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <7vr6ncrh22.fsf@assigned-by-dhcp.cox.net>

On Thu, Jul 12, 2007 at 10:47:17PM -0700, Junio C Hamano wrote:
> Greg KH <greg@kroah.com> writes:
> 
> > When using git-send-email.perl on a changeset that has:
> > 	Cc: <stable@kernel.org>
> > in the body of the description, and the Email::Valid perl module is
> > installed on the system, the email address will be deemed "invalid" for
> > some reason (Email::Valid isn't smart enough to handle this?) and
> > complain and not send the address the email.
> 
> That appears to be the case.
> 
>         bad foo
>         bad <foo@bar.baz>
>         ok  foo@bar.baz
>         ok  Foo <foo@bar.baz>
> 
> > Anyway, this tiny patch fixes this problem for me.  Note, my perl-foo is
> > quite week, so this could probably be easily done in one line for those
> > with better reg-ex skills.
> 
> > --- a/git-send-email.perl
> > +++ b/git-send-email.perl
> > @@ -410,6 +410,9 @@ sub extract_valid_address {
> >  	return $address if ($address =~ /^($local_part_regexp)$/);
> >  
> >  	if ($have_email_valid) {
> > +		if ($address =~ s/^<//) {
> > +			$address =~ s/>$//;
> > +		}
> >  		return scalar Email::Valid->address($address);
> >  	} else {
> 
> I'd probably do:
> 
> 	if ($have_email_valid) {
> 		$address =~ s/^<(.*)>$/$1/;

Ah, yeah, that looks better :)

Thanks for the fix,

greg k-h

^ permalink raw reply

* Re: Lump commit HOWTO?
From: martin f krafft @ 2007-07-13  6:09 UTC (permalink / raw)
  To: Git Mailing List
In-Reply-To: <625fc13d0707121821l70e2d6aaw555c79b5d700585f@mail.gmail.com>

[-- Attachment #1: Type: text/plain, Size: 692 bytes --]

also sprach Josh Boyer <jwboyer@gmail.com> [2007.07.13.0321 +0200]:
> 8) take all local commits (A - D) and create a single commit without
> any of the upstream changes in it

This is entirely untested and since I am a newbie, it might well not
work, but shouldn't the following do what you want?

git log origin..master --> gives you the local commits not in origin
git checkout -b merger
git merge --squash <sha> <sha> <sha> ...
git commit ...

-- 
martin;              (greetings from the heart of the sun.)
  \____ echo mailto: !#^."<*>"|tr "<*> mailto:" net@madduck
 
spamtraps: madduck.bogus@madduck.net
 
officer, arrest that man! he's whistling a copyrighted song.

[-- Attachment #2: Digital signature (GPG/PGP) --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply

* What's in git.git
From: Junio C Hamano @ 2007-07-13  6:06 UTC (permalink / raw)
  To: git
In-Reply-To: <7vk5tj3bj1.fsf@assigned-by-dhcp.cox.net>

Executive summary:

 * (maint) hopefully the last maintenance release for v1.5.2
   codebase, v1.5.2.4, is out.

 * (master) v1.5.3 is nicely progressing and we have v1.5.3-rc1
   out, but it has a serious last minute glitch in pack-objects,
   so please do not use "git-gc", or "git-repack" from vanilla
   v1.5.3-rc1.

   v1.5.3-rc1-1-g7d7baa5 or later should be Ok.

 * (next/pu) No topics are cooking in 'next' right now, although
   I might apply a few series I did not pick up from the list in
   the past few days, just to keep them from getting lost.

As usual, v1.5.3-rc1 means:

 * I personally will be using 'master' version for my work until
   v1.5.3 final (I usually run 'next', and switch to 'master'
   after -rc0); I ask contributors to do the same to shake out
   the last minute bugs from 'master'.

 * No more features and large code churning on 'master' until
   v1.5.3 final.

 * Bugfixes and documenation updates are always welcomed, but
   even more so than usual until v1.5.3 final.

I'll send out a draft release notes for v1.5.3 in a separate
message.

----------------------------------------------------------------

* The 'maint' branch spawned 1.5.2.4 with accumulated fixes.
  Most notably, we are in sync with git-gui 0.7.5.

* The 'master' branch has these since the last announcement; 
  we are at v1.5.3-rc1 plus a few fixes.

Adam Roben (1):
  format-patch: Add format.subjectprefix config option

Alecs King (1):
  fix remote.origin.url in tutorial.txt

Alex Riesen (4):
  Handle missing prefix for "Subject:" as if no prefix given
  Handle format.subjectprefix for every command which accepts
      --pretty
  Fix t5516 to create test repo without hooks
  Add -v|--verbose to git remote to show remote url

Andrew Ruder (2):
  Remove USE_PAGER from git-pickaxe and git-annotate
  Add urls.txt to git-clone man page

Brian Downing (10):
  pack-objects: Prefer shallower deltas if the size is equal
  gitk: Fix for tree view ending in nested directories
  Pack information tool
  Correct shebang line for contrib/stats/packinfo.pl
  Don't try to delta if target is much smaller than source
  Support fetching the memory usage of a delta index
  Add functions for parsing integers with size suffixes
  Add pack-objects window memory usage limit
  Add --window-memory option to git-repack
  Add documentation for --window-memory, pack.windowMemory

Brian Gernhardt (1):
  Add core.pager config variable.

CJ van den Berg (1):
  git-submodule: Fix two instances of the same typo

Carlos Rica (5):
  t7004: Skip tests for signed tags in an old version of gpg.
  t0030: Remove repeated instructions and add missing &&
  t0030: Add tests with consecutive text lines and others with spaces
      added.
  t7004: Add tests for the git tag -n option.
  Function stripspace now gets a buffer instead file descriptors.

Daniel Barkalow (2):
  Add allocation and freeing functions for struct refs
  Some cosmetic changes to remote library

David Kastrup (1):
  Add missing functions to contrib/emacs/vc-git.el

Emil Medve (1):
  git-submodule: Instead of using only annotated tags, use any tags.

Eric Wong (2):
  git-svn: allow dcommit to retain local merge information
  git-svn: fix blocking with svn:// servers after do_switch

Frank Lichtenheld (1):
  cvsserver: always initialize state in argsplit()

Gerrit Pape (1):
  git-commit: don't add multiple Signed-off-by: from the same
      identity

Jakub Narebski (3):
  Update git-merge documentation.
  Document long options '--message=<msg>' and '--no-commit'
  Document git commit --untracked-files and --verbose

James Bowes (1):
  stash: allow running from a subdirectory

Jeff King (6):
  git-stash: fix "no arguments" case in documentation
  git-stash: fix "can't shift that many" with no arguments
  git-stash: don't complain when listing in a repo with no stash
  Documentation: quote {non-attributes} for asciidoc
  Documentation: quote {non-attributes} for asciidoc
  Documentation: minor cleanups to branch/checkout wording

Jeffrey C. Ollie (2):
  Add an option to quiet git-init.
  Quiet the output from git-init when cloning, if requested.

Johannes Schindelin (28):
  Move the pick_author code to git-sh-setup
  Teach rebase an interactive mode
  rebase -i: several cleanups
  rebase -i: provide reasonable reflog for the rebased branch
  Teach rebase -i about --preserve-merges
  Make '!' aliases more useful
  git-fsck: add --lost-found option
  Document git-filter-branch
  Add diff-option --ext-diff
  filter-branch: add a test for the commit removal example
  filter-branch: make output nicer
  filter-branch: a few more touch ups to the man page
  filter-branch documentation: clarify which filters are eval'ed
  filter-branch: fail gracefully when a filter fails
  Future-proof source for changes in xdemitconf_t
  Teach git-stash to "apply --index"
  Enable "git rerere" by the config variable rerere.enabled
  git-branch: default to --track
  branch.autosetupmerge: allow boolean values, or "all"
  rebase -i: handle --continue more like non-interactive rebase
  rebase -i: actually show the diffstat when being verbose
  rebase -i: remember the settings of -v, -s and -p when interrupted
  rebase -i: put a nice warning into the todo list
  rerere: record resolution even if file is not in merge base
  Fix core.sharedRepository = 2
  Fix --cherry-pick with given paths
  Add for_each_remote() function, and extend remote_find_tracking()
  branch --track: code cleanup and saner handling of local branches

Johannes Sixt (4):
  Test 'git add' for unmerged entries when core.symlinks=false.
  filter-branch: Avoid an error message in the map function.
  filter-branch documentation: some more touch-ups.
  Allow rebase to run if upstream is completely merged

Jonas Fonseca (1):
  fsck --lost-found writes to subdirectories in .git/lost-found/

Junio C Hamano (30):
  diffcore_count_changes: pass diffcore_filespec
  diffcore_filespec: add is_binary
  diffcore-delta.c: update the comment on the algorithm.
  diffcore-delta.c: Ignore CR in CRLF for text files
  git-stash: require "save" to be explicit and update documentation
  Update public documentation links for 1.5.2.3
  "git-push $URL" without refspecs pushes only matching branches
  Rewrite "git-frotz" to "git frotz"
  git-stash: make "save" the default action again.
  Mark disused commit walkers officially deprecated.
  Update draft Release Notes for 1.5.3
  Update reflog message created for stashes
  Do not check if getcwd() result begins with a slash.
  Fix git-stash(1) markup.
  git-stash: allow more descriptive reminder message when saving
  Introduce diff_filespec_is_binary()
  Per-path attribute based hunk header selection.
  Fix configuration syntax to specify customized hunk header
      patterns.
  diff: honor binariness specified in attributes
  gitweb: make repeated calls to git_get_project_owner() bearable
  diff.c: make built-in hunk header pattern a separate table
  git-gui: use "blame -w -C -C" for "where did it come from,
      originally?"
  git-stash: try reusing cached stat info as much as possible
  Fix merge-one-file for our-side-added/our-side-removed cases
  Document custom hunk header selection
  revision.c: remove duplicated parents after history simplification
  Revert 88494423 (removal of duplicate parents in the output
      codepath)
  Re-code builtin-branch.c in UTF-8
  Update list of older git docs
  GIT v1.5.3-rc1

Lars Hjemli (1):
  git-submodule(1): update description and key names

Linus Torvalds (1):
  Start deprecating "git-command" in favor of "git command"

Marcus Fritzsch (1):
  Fixed a formulation mistake in Documentation/user-manual.txt

Matt Kraai (3):
  Prefer EMAIL to username@hostname.
  Change "added.moved or removed" to "added, moved or removed" in
  Add [verse] to the SYNOPSIS section of git-submodule.txt.

Matt McCutchen (3):
  gitweb: make search form generate pathinfo-style URLs
  gitweb: make "No commits" in project list gray, not bold green
  Makefile: rebuild git.o on version change, clean up git$X flags

Matthias Lederhofer (5):
  ignore git-rebase--interactive
  getenv/setenv: use constants if available
  git-init: set core.worktree if GIT_WORK_TREE is specified
  git-clone: split up long &&-command-chain and use a function for
      cleanup
  make git-clone GIT_WORK_TREE aware

Michael Hendricks (2):
  git-send-email: allow an email alias for --from
  gitweb: configurable width for the projects list Description column

Miklos Vajna (2):
  gitweb: prefer git_get_project_owner() over get_file_owner()
  gitweb: new cgi parameter: opt

Nanako Shiraishi (2):
  Add git-stash script
  Document git-stash

Nicolas Pitre (4):
  apply delta depth bias to already deltified objects
  script to display a distribution of longest common hash prefixes
  reduce git-pack-objects memory usage a little more
  Pack-objects: properly initialize the depth value

Paul Mackerras (6):
  gitk: Fix the find and highlight functions
  gitk: Fix bug in the anc_or_desc routine
  gitk: Remove the unused stopfindproc function
  gitk: Fix bug causing "can't read commitrow(0,n)" error
  gitk: Use git log and add support for --left-right
  gitk: Improve handling of -- and ambiguous arguments

René Scharfe (1):
  diff-lib.c: don't strdup twice

Sean Estabrooks (1):
  Alter git-checkout reflog message to include "from" branch

Shawn O. Pearce (36):
  git-gui: Start blame windows as tall as possible
  git-gui: Correct resizing of remote branch delete dialog
  git-gui: Honor rerere.enabled configuration option
  git-gui: New Git version check support routine
  git-gui: Teach class system to support [$this cmd] syntax
  git-gui: Abstract the revision picker into a mega widget
  git-gui: Refactor the delete branch dialog to use class system
  git-gui: Optimize for newstyle refs/remotes layout
  git-gui: Maintain remote and source ref for tracking branches
  git-gui: Allow users to match remote branch names locally
  git-gui: Fast-forward existing branch in branch create dialog
  git-gui: Enhance choose_rev to handle hundreds of branches
  git-gui: Sort tags descending by tagger date
  git-gui: Option to default new branches to match tracking branches
  git-gui: Automatically refresh tracking branches when needed
  git-gui: Better handling of detached HEAD
  git-gui: Refactor our ui_status_value update technique
  git-gui: Refactor branch switch to support detached head
  git-gui: Unabbreviate commit SHA-1s prior to display
  git-gui: Default selection to first matching ref
  git-gui: Allow double-click in checkout dialog to start checkout
  git-gui: Extract blame viewer status bar into mega-widget
  git-gui: Change the main window progress bar to use status_bar
  git-gui: Show a progress meter for checking out files
  git-gui: Always use absolute path to all git executables
  git-gui: Correct gitk installation location
  git-gui: Assume unfound commands are known by git wrapper
  git-gui: Treat `git version` as `git --version`
  git-gui: Perform our own magic shbang detection on Windows
  git-gui: Teach console widget to use git_read
  git-gui: Improve the Windows and Mac OS X shortcut creators
  git-gui: Paper bag fix for Cygwin shortcut creation
  git-gui: Use sh.exe in Cygwin shortcuts
  git-gui: Include a space in Cygwin shortcut command lines
  Support wholesale directory renames in fast-import
  git-gui: Change prior tree SHA-1 verification to use git_read

Steffen Prohaska (1):
  filter-branch: added missing warn function

Steven Walter (1):
  Documentation for git-log --follow

Sven Verdoolaege (2):
  git-submodule: provide easy way of adding new submodules
  git-clone: fetch possibly detached HEAD over dumb http

Uwe Kleine-König (2):
  stash: end commit log with a newline
  repack: don't report "Nothing new to pack." if -q is given

^ permalink raw reply

* Re: pull-fetch-param.txt
From: Gerrit Pape @ 2007-07-13  5:53 UTC (permalink / raw)
  To: Junio C Hamano, git
In-Reply-To: <7vvecps2rz.fsf@assigned-by-dhcp.cox.net>

On Thu, Jul 12, 2007 at 02:58:08PM -0700, Junio C Hamano wrote:
> Gerrit Pape <pape@smarden.org> writes:
> > Hi, this still is a problem, at least on Debian/unstable; with asciidoc
> > 8.2.1, the git-push(1) and git-fetch(1) man pages have this 'broken'
> > refspec description[0].
> 
> Quick question.  Is the build done with "make
> ASCIIDOC8=YesPlease"?

No, must have missed that.  This solves the first issue with the refspec
in git-push(1), git-fetch(1), but not the second one with callout lists.

Thanks, Gerrit.

^ permalink raw reply

* Re: [PATCH] make git-send-email.perl handle email addresses with no names when Email::Valid is present
From: Junio C Hamano @ 2007-07-13  5:47 UTC (permalink / raw)
  To: Greg KH; +Cc: git
In-Reply-To: <20070713041749.GA28824@kroah.com>

Greg KH <greg@kroah.com> writes:

> When using git-send-email.perl on a changeset that has:
> 	Cc: <stable@kernel.org>
> in the body of the description, and the Email::Valid perl module is
> installed on the system, the email address will be deemed "invalid" for
> some reason (Email::Valid isn't smart enough to handle this?) and
> complain and not send the address the email.

That appears to be the case.

        bad foo
        bad <foo@bar.baz>
        ok  foo@bar.baz
        ok  Foo <foo@bar.baz>

> Anyway, this tiny patch fixes this problem for me.  Note, my perl-foo is
> quite week, so this could probably be easily done in one line for those
> with better reg-ex skills.

> --- a/git-send-email.perl
> +++ b/git-send-email.perl
> @@ -410,6 +410,9 @@ sub extract_valid_address {
>  	return $address if ($address =~ /^($local_part_regexp)$/);
>  
>  	if ($have_email_valid) {
> +		if ($address =~ s/^<//) {
> +			$address =~ s/>$//;
> +		}
>  		return scalar Email::Valid->address($address);
>  	} else {

I'd probably do:

	if ($have_email_valid) {
		$address =~ s/^<(.*)>$/$1/;
		return scalar Email::Valid->address($address);
	} else {

instead, but they are moral equivalents.

Thanks for a fix.

^ permalink raw reply

* Re: sharing between local "work" and "nightly build" git repos
From: Alex Riesen @ 2007-07-13  5:11 UTC (permalink / raw)
  To: David Frech; +Cc: Junio C Hamano, git
In-Reply-To: <7154c5c60707121733r6584a407r8d60d5890b9c89e2@mail.gmail.com>

David Frech, Fri, Jul 13, 2007 02:33:19 +0200:
> On 7/12/07, David Frech <nimblemachines@gmail.com> wrote:
> >On 7/12/07, Junio C Hamano <gitster@pobox.com> wrote:
> >> Then a nightly update would go like this:
> >>
> >>  $ cd ~/nightly-git
> >>  $ git pull origin next
> >>  $ make clean
> >>  $ make test || barf
> 
> One more thing: would it make sense to do "make -k test" so that *all*
> failures (if >1) show up?

Yes, definitely. You'll find you will see failures which you know are
already fixed, just didn't reach the nightly-build yet.

BTW, do _not_ send the errors to this mailing list. It just too easy
gets out of control. And consider saving the build log somewhere after
doing a _completely_ clean build, i.e. like this:

    #!/bin/bash
    cd ~/nightly-git || exit
    echo 'Subject: Nightly build git next ' $(date) >/tmp/mail.msg
    echo >>/tmp/mail.msg
    {
	rm -rf * # it does not remove .git
	git reset --hard
	git pull origin next || exit 1
	make || exit 1
	make -k test|| exit 1
    } &>> /tmp/mail.msg

    $ nightly-build || sendmail local-user@localhost < /tmp/mail.msg

It is simplier to find out what went wrong if you know what state did
you have before doing the test. And the state after rm -rf * is very
simple to predict.

^ permalink raw reply

* Re: Lump commit HOWTO?
From: Alex Riesen @ 2007-07-13  4:54 UTC (permalink / raw)
  To: Josh Boyer; +Cc: Git Mailing List
In-Reply-To: <625fc13d0707121821l70e2d6aaw555c79b5d700585f@mail.gmail.com>

Josh Boyer, Fri, Jul 13, 2007 03:21:17 +0200:
> Hi All,
> 
> I have a specific workflow in mind that I'm not entirely sure how to
> accomplish with git.  What I'd like to do is track a project in a
> local branch, and do commits of my own there as well.  Then when I'm
> ready to submit the work, I want to take all the incremental commits
> and lump them into a single new commit and push that out as a patch or
> into a branch for people to pull from.

See git merge --squash. It is exactly that.
Also git-rebase --interactive and git cherry-pick -n can help you to
get the same.

^ permalink raw reply

* [PATCH] make git-send-email.perl handle email addresses with no names when Email::Valid is present
From: Greg KH @ 2007-07-13  4:17 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

When using git-send-email.perl on a changeset that has:
	Cc: <stable@kernel.org>
in the body of the description, and the Email::Valid perl module is
installed on the system, the email address will be deemed "invalid" for
some reason (Email::Valid isn't smart enough to handle this?) and
complain and not send the address the email.

Anyway, this tiny patch fixes this problem for me.  Note, my perl-foo is
quite week, so this could probably be easily done in one line for those
with better reg-ex skills.


Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -410,6 +410,9 @@ sub extract_valid_address {
 	return $address if ($address =~ /^($local_part_regexp)$/);
 
 	if ($have_email_valid) {
+		if ($address =~ s/^<//) {
+			$address =~ s/>$//;
+		}
 		return scalar Email::Valid->address($address);
 	} else {
 		# less robust/correct than the monster regexp in Email::Valid,

^ permalink raw reply

* Re: mtimes of working files
From: Theodore Tso @ 2007-07-13  0:37 UTC (permalink / raw)
  To: David Woodhouse; +Cc: Andy Parkins, git, Jan Hudec, Johannes Schindelin
In-Reply-To: <1184261246.31598.139.camel@pmac.infradead.org>

On Thu, Jul 12, 2007 at 06:27:26PM +0100, David Woodhouse wrote:
> On Thu, 2007-07-12 at 08:57 +0100, Andy Parkins wrote:
> > The only time you get an unnecessary rebuild is if you do
> > 
> >  git checkout branch1
> >  git checkout branch2
> >  git checkout branch1
> > 
> > But we can hardly expect git to be responsible for that. 
> 
> Indeed. That's a user error. Git makes it cheap and easy to have
> separate _trees_. Just use them -- branches are just another mental
> hangover from CVS which we should try to cure ourselves of :)

Personally, I just use branches a huge amount, and I will often do

git checkout branch1
<hack hack hack>
git commit --amend
<build, test>
git checkout branch2
<hack hack hack>
git commit
<build, test>
git checkout branch1
<build>

Rebuilding isn't a problem, because I use ccache.  :-)

I could use separate trees, I suppose, but then I have to keep
multiple copies of the .o files around in all of those separate trees,
and it's cheaper and more efficient to keep them in the ccache cache
IMHO.  And with 7200 RPM laptop drives and dual core processors
combined with ccache, I hardly notice the rebuild/relink time.

	     	       	      	     - Ted

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox