Git development
 help / color / mirror / Atom feed
* Re: [Census] So who uses git?
From: linux @ 2006-02-01  7:08 UTC (permalink / raw)
  To: torvalds; +Cc: git, linux

> Yes, I think the "assume unchanged" flag goes well together with making 
> sure that the checked-out file is non-writable at the time.
> 
> Of course, any number of editors and other actions won't care: if you do 
> anything like
> 
> 	for i in *.c
> 	do
> 		sed 's/xyzzy/bas/g' < $i > $i.new
> 		mv $i.new $i
> 	done
> 
> you'll never have even noticed that the old file was marked read-only. So 
> it's obviously not in any way any guarantee, but it probably makes sense 
> as a crutch.

At the risk of complicating something already very complicated, and
possibly breaking on Microsoft file systems, that case can be detected
by reading the directory and noticing that the inode number changed.

Would it be worth validating the inode numbers (which can be retrieved
in a batch) even if you don't do a full lstat()?

Or is that too Unix-centric and prone to performance problems on other
file systems?  I'd think that, even if a file system used fake inode
numbers, they'd be pretty consistent if you didn't touch the file at all,
and being different would just cause a more expensive validation.
Which would be okay as long as it's infrequent.

^ permalink raw reply

* Re: [Census] So who uses git?
From: Carl Worth @ 2006-02-01  7:22 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Linus Torvalds, git
In-Reply-To: <7v4q3jlgw2.fsf@assigned-by-dhcp.cox.net>

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

On Tue, 31 Jan 2006 22:42:05 -0800, Junio C Hamano wrote:
> 
> There is no question we do not commit "another-file" and we do
> commit changes to the "existing-file" as a whole.  What should
> we do to "a-new-file", and how do we explain why we do so to
> novices?

I'll offer a couple of ill-informed comments from a novice's
point-of-view if I may.

My first exposure to git (about 1 week ago) was "A short git
tutorial" [*]

I found the discussion of the index, git-update-index, and the subtle
distinctions between the various git-diff commands rather intimidating
for an initial introduction. After getting to know the system better
over the past week, it seems it should be possible to have a class of
"novice ready" tools that provide for common use cases and that never
require any mention of the index in their documentation. If so, that
seems to me a useful goal to work toward and a useful guide in this
discussion.

> We could make "git commit" without paths to mean the current
> "-a" behaviour, which would match CVS behaviour more closely.

Again, my novice experience leads me to favor that change. After
reading the tutorial, I had the following sequence in mind for
committing an edited file:

	git update-index edited-file
	git commit

which seemed like more pain than strictly necessary. The next day,
when I went to the linux.conf.au tutorial and saw Linus use:

	git commit -a

for the same operation it was a breath of fresh air. I was left
scratching my head wondering why the -a behavior wasn't the default
for "git commit" with no paths.

> However, it would make commit after a merge conflict resolution
> in a dirty working tree _very_ dangerous -- it may give more
> familiar feel to CVS people, but it is not an improvement for
> git people at all.  I would rather not.

I'm still not "git people" I guess. Could you explain what the danger
is here? And is it something the tool could detect and prevent?

-Carl

[*] http://www.kernel.org/pub/software/scm/git/docs/core-tutorial.html [*

A better initial introduction for me would likely have been "A
tutorial introduction to git":

http://www.kernel.org/pub/software/scm/git/docs/tutorial.html

so a link to the latter from the first paragraph or so of the former
might be very helpful.


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

^ permalink raw reply

* undoing changes with git-checkout -f
From: lamikr @ 2006-01-09 21:16 UTC (permalink / raw)
  To: git

Hi

Can somebody tell have I understood git-checkout -f wrong as following
does not work as I thought
1) I clone git repo by using command

    git-clone rsync://source.mvista.com/git/linux-omap-2.6.git
linux-omap-2.6

2) I go to cloned repo and create there a new file
    cd linux-omap-2.6
    echo "test" > 1.txt

3) I want to undo the creation of 1.txt by using command
    git-checkout -f

but for some reason the 1.txt is still displayed in the root of
linux-omap-2.6 directory. (I have also tried "git-reset --hard" but
seems to have same effect)
What am I doing wrong?

Mika

^ permalink raw reply

* Re: [Census] So who uses git?
From: Junio C Hamano @ 2006-02-01  8:26 UTC (permalink / raw)
  To: Carl Worth; +Cc: Linus Torvalds, git
In-Reply-To: <87vevzpmql.wl%cworth@cworth.org>

Carl Worth <cworth@cworth.org> writes:

> ... it seems it should be possible to have a class of
> "novice ready" tools that provide for common use cases and that never
> require any mention of the index in their documentation. If so, that
> seems to me a useful goal to work toward and a useful guide in this
> discussion.

I agree it is a worthy goal.  Unfortunately I lost my git
virginity long time ago, so a fresh perspective is really
appreciated in this discussion.

> ... Could you explain what the danger is here?

As Linus mentioned in an earlier message in this thread, one of
the important task for him is to take other peoples' trees and
merge it into his mainline.  The workflow goes like this:

	$ git pull from-somewhere
        ... oops there are conflicts
        $ edit conflicted/file
        $ edit more/conflicted/file
        ... maybe compile test ...
	$ git diff -c ;# final sanity check
        $ git update-index conflicted/file
        $ git update-index more/conflicted/file
        $ git commit

He does *not* want to do "git commit -a" here, because he
usually has unrelated changes in his working tree he has not
done update-index on and does _not_ want to commit [*1*].  "git
commit" to imply "git commit -a" increases the risk of
accidentally committing those unrelated changes mixed in the
merge (eh, actually makes the risk 100%).

We _could_ detect that we were in the middle of a merge,
enumerate the paths touched by the merged branches.  Then we can
say paths that are different between the index and the working
tree and not in the paths touched by the merge are his unrelated
changes.  But it is conceivable he may need to modify a file
neither branch touches in order to _logically_ resolve the
merge, even when the merge phisically does not conflict in
textual diff basis, so while that heuristics may work pretty
well most of the time, doing so might make things even less
easier to explain to other people.


[Footnotes]

*1* The reason he has unrelated changes while doing a merge is
because he works on things himself (I am speculating about
this), and for these modified paths he never runs git-add nor
git-update-index until he is ready to commit his changes (I am
not speculating about this).  As long as he knows what he is
pulling in from outside does not overlap with what he has been
working on, he can merge and commit the result without worrying
about his own unrelated changes, and git is careful not to touch
anything in his working tree to cause information loss when the
changes do overlap [*2*].

He is committing something that he never tested himself in his
working tree as a whole.  The tree resulting from the merge
never existed outside his index file, so there is no way he
could have even compile tested it properly.  But for somebody
who is playing an integrator's role, it is not his primary job
to examine and test every change he merges in as a whole at
nitty-gritty level -- that is what the originator of the change
should have done.  So having uncommitted changes in the working
tree for an integrator person is not a sign of bad discipline at
all, and supporting this workflow _is_ important for git.

The primary reason I first got involved in git was because I
wanted to help the workflow of the kernel people, especially
Linus and the subsystem maintainers.  To be honest, I personally
still consider the kernel people the first tier customers for
me, and I stop and try to think twice when thinking about a
change or a new feature that may help individual developers and
newcomers, to make sure such a change does not make life less
convenient for the 'integrator' people.  Helping integrators to
be more efficient is important because they can become
bottlenecks.

*2* I once got yelled at by Linus when I carelessly broke this
feature and changed 'git-merge' to require a clean working tree
without changes before starting a merge; it was quickly
reverted.

^ permalink raw reply

* [ANNOUNCE] Cogito-0.16.4
From: Petr Baudis @ 2006-01-17 19:48 UTC (permalink / raw)
  To: git
In-Reply-To: <20060117080736.12033.qmail@4af7d036c9451c.315fe32.mid.smarden.org>

Dear diary, on Tue, Jan 17, 2006 at 09:07:36AM CET, I got a letter
where Gerrit Pape <pape@smarden.org> said that...
> On Mon, Jan 16, 2006 at 02:51:41AM +0100, Petr Baudis wrote:
> >   Hello,
> > 
> >   this is Cogito version 0.16.3, the next stable release of the
> > human-friendly version control UI for the Linus' GIT tool. Share
> > and enjoy at:
> > 
> > 	http://www.kernel.org/pub/software/scm/cogito/
> 
> Hi, the cg-clone program in the tarball has permissions 666, breaking
> the selftest t9105, unless you have another cg-clone program in PATH;
> and if so, the other cg-clone is tested instead of the new one.
> 
>  $ wget -q -O- kernel.org/pub/software/scm/cogito/cogito-0.16.3.tar.gz | 
>    tar tzvf - cogito-0.16.3/cg-clone
>  -rw-rw-rw- git/git        2483 2006-01-16 01:39:11 cogito-0.16.3/cg-clone

Good catch, thanks. I've just released a quickfix Cogito version 0.16.4,
fixing this and a spurious read when committing a squash merge reported
by Jonas Fonseca.

-- 
				Petr "Pasky" Baudis
Stuff: http://pasky.or.cz/
Of the 3 great composers Mozart tells us what it's like to be human,
Beethoven tells us what it's like to be Beethoven and Bach tells us
what it's like to be the universe.  -- Douglas Adams

^ permalink raw reply

* Re: undoing changes with git-checkout -f
From: Junio C Hamano @ 2006-01-09 21:46 UTC (permalink / raw)
  To: lamikr; +Cc: git
In-Reply-To: <43C2D2C4.2010904@cc.jyu.fi>

lamikr <lamikr@cc.jyu.fi> writes:

> 1) I clone git repo by using command
>
>     git-clone rsync://source.mvista.com/git/linux-omap-2.6.git
> linux-omap-2.6

Please do not use rsync:// transport if possible (mvista might
only pubilsh via rsync:// and not git://, so it may not be your
fault).

> 2) I go to cloned repo and create there a new file
>     cd linux-omap-2.6
>     echo "test" > 1.txt
>
> 3) I want to undo the creation of 1.txt by using command
>     git-checkout -f
>
> but for some reason the 1.txt is still displayed in the root of
> linux-omap-2.6 directory. (I have also tried "git-reset --hard" but
> seems to have same effect)
> What am I doing wrong?

Nothing.  After the second step, git does not know anything
about 1.txt; if it is a part of something you wanted to
eventually commit, or it is some notes you took while perusing
the source and is precious even when you switch branches (even
though you would not commit it as part of the project) , so it
does not touch it.  After running "make", "checkout -f" does not
do "make clean" for you to remove *.o files either, for exactly
the same reason.

"git status" would tell you the file is "untracked".

If you did something like this:

	$ edit 1.txt
        $ git add 1.txt
        $ git reset --hard

"git reset --hard" would remove it, while "git checkout -f"
would leave the file behind.

BTW, please do not set Reply-To: (or Mail-Followup-To: for that
matter) to the list.  When I (or somebody else) want to reply
to you, especially in private, your Reply-To: header forces me
to manually rewrite the To: header MUA prepares for me.

I know why you do it --- you are on the list and otherwise you
would get duplicate messages, one from me directly and another
from the list.  I've seen other people do it, but IMNSHO it is a
bad practice.  Filter them on your end, and do not put extra
burden to others, please.  The only case mucking with the
addressee headers may be acceptable is to remove yourself from
CC: list when a list you are on is on the CC: list.

^ permalink raw reply

* RE: git pull on Linux/ACPI release tree
From: Linus Torvalds @ 2006-01-09 16:57 UTC (permalink / raw)
  To: Brown, Len
  Cc: Luck, Tony, Junio C Hamano, Martin Langhoff, David S. Miller,
	linux-acpi, linux-kernel, akpm, git
In-Reply-To: <Pine.LNX.4.64.0601090835580.3169@g5.osdl.org>



On Mon, 9 Jan 2006, Linus Torvalds wrote:
>
> One thing we could do is to make it easier to apply a patch to a 
> _non_current_ branch.
>   [ ... ]
> Do you think that kind of workflow would be more palatable to you? It 
> shouldn't be /that/ hard to make git-apply branch-aware... (It was part of 
> my original plan, but it is more work than just using the working 
> directory, so I never finished the thought).

Btw, this is true in a bigger sense: the things "git" does have largely 
been driven by user needs. Initially mainly mine, but things like 
"git-rebase" were from people who wanted to work as "sub-maintainers" (eg 
Junio before he became the head honcho for git itself).

But if there are workflow problems, let's try to fix them. The "apply 
patches directly to another branch" suggestion may not be sane (maybe it's 
too confusing to apply a patch and not actually see it in the working 
tree), but workflow suggestions in general are appreciated.

We've made switching branches about as efficient as it can be (but if the 
differences are huge, the cost of re-writing the working directory is 
never going to be low). But switching branches has the "confusion factor" 
(ie you forget which branch you're on, and apply a patch to your working 
branch instead of your development branch), so maybe there are other ways 
of doing the same thing that might be sensible..

So send suggestions to the git lists. Maybe they're insane and can't be 
done, but while I designed git to work with _my_ case (ie mostly merging 
tons of different trees and then having occasional big batches of 
patches), it's certainly _supposed_ to support other maintainers too..

		Linus

^ permalink raw reply

* Re: [Census] So who uses git?
From: Junio C Hamano @ 2006-02-01  8:51 UTC (permalink / raw)
  To: linux; +Cc: git
In-Reply-To: <20060201070847.2021.qmail@science.horizon.com>

linux@horizon.com writes:

> At the risk of complicating something already very complicated, and
> possibly breaking on Microsoft file systems, that case can be detected
> by reading the directory and noticing that the inode number changed.
>
> Would it be worth validating the inode numbers (which can be retrieved
> in a batch) even if you don't do a full lstat()?

I suspect that what you said about Microsoft filesystems is even
stronger. IIRC the latest Cygwin stopped giving d_ino regardless
of the filesystem type; you need to do a stat() anyway.

^ permalink raw reply

* RE: git pull on Linux/ACPI release tree
From: Linus Torvalds @ 2006-01-09 16:47 UTC (permalink / raw)
  To: Brown, Len
  Cc: Luck, Tony, Junio C Hamano, Martin Langhoff, David S. Miller,
	linux-acpi, linux-kernel, akpm, git
In-Reply-To: <F7DC2337C7631D4386A2DF6E8FB22B3005A13706@hdsmsx401.amr.corp.intel.com>



On Mon, 9 Jan 2006, Brown, Len wrote:
> >
> >The huge majority of my "automatic update from upstream" merges
> >go into my test branch ... which never becomes part of the real
> >history as I never ask Linus to pull from it.
> >
> >-Tony
> >
> >[1] Sometimes I goof on this because I forget that I've applied
> >a trivial patch directly to the release branch without going through
> >a topic branch.  I think I'll fix my update script to check 
> >for this case.
> 
> I figured that checking some trivial patches directly into "release"
> would be a convenient way to make sure I didn't forget to push them --
> as they didn't depend on anything else in my tree.  Okay.

One thing we could do is to make it easier to apply a patch to a 
_non_current_ branch.

In other words, let's say that we want to encourage the separation of a 
"development branch" and a "testing and use" branch (which I'd definitely 
personally like to encourage people to do).

And one way to do that might be to teach "git-apply" to apply patches to a 
non-active branch, and then you keep the "testing and use" branch as your 
_checked_out_ branch (and it's going to be really dirty), but when you 
actually apply patches you could do that to the "development" branch with 
something like

	git-apply -b development < patch-file

(Now, of course, that's only if you apply somebody elses patch - if you 
actually do development _yourself_, you'd either have to check out the 
development branch and do it there, or you'd move the patch you have in 
your "ugly" checked-out testing branch into the development branch with

	git diff | git-apply -b development

or something similar..)

Then you could always do "git pull . development" to pull in the 
development stuff into your working branch - keeping the development 
branch clean all the time.

Do you think that kind of workflow would be more palatable to you? It 
shouldn't be /that/ hard to make git-apply branch-aware... (It was part of 
my original plan, but it is more work than just using the working 
directory, so I never finished the thought).

> I'm hopeful that gitk users will not be irritated also
> by the liberal use of topic branches.

"gitk" is actually pretty good at showing multiple branches. Try doing a

	gitk --all -d

and you'll see all the topic branches in date order. The "-d" isn't 
strictly necessary, and to some degree makes the output messier by 
interleaving the commits from different branches, so you may not want to 
do it, but it is sometimes nice to see the "relative dates" of individual 
commits rather than the denser format that gitk defaults to.

> In the case where a topic branch is a single commit, gitk users
> will see both the original commit, as well as the merge commit
> back into "release".

Yes, topic branches will always imply more commits, but I think they are 
of the "nice" kind.

I definitely encourage people to use git as a distributed concurrent 
development system ratehr than the "collection of patches" thing. Quilt is 
much better at the collection of patches. 

So I'd encourage topic branches - even within something like ACPI, you 
might have separate topics ("interpreter" branch vs "x86" branch vs 
"generic-acpi" branch).

And yes, that will make history sometimes messier too, and it will cause 
more merges, but the difference there is that the merges will be 
meaningful (ie merging the "acpi interpreter" branch into the generic ACPI 
branch suddenly has _meaning_, even if there only ends up being a couple 
of commits per merge).

Ok?

		Linus

^ permalink raw reply

* Re: [Census] So who uses git?
From: Randal L. Schwartz @ 2006-02-01  9:59 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Carl Worth, Linus Torvalds, git
In-Reply-To: <7virrzfpse.fsf@assigned-by-dhcp.cox.net>

>>>>> "Junio" == Junio C Hamano <junkio@cox.net> writes:

Junio> *1* The reason he has unrelated changes while doing a merge is
Junio> because he works on things himself (I am speculating about
Junio> this),

You need to speculate that Linus works on things himself? :)

-- 
Randal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095
<merlyn@stonehenge.com> <URL:http://www.stonehenge.com/merlyn/>
Perl/Unix/security consulting, Technical writing, Comedy, etc. etc.
See PerlTraining.Stonehenge.com for onsite and open-enrollment Perl training!

^ permalink raw reply

* Re: gnu/linux sued for predatory business practices
From: Petr Baudis @ 2006-02-01 10:38 UTC (permalink / raw)
  To: walt; +Cc: git
In-Reply-To: <drp5vk$njt$1@sea.gmane.org>

Dear diary, on Wed, Feb 01, 2006 at 03:22:06AM CET, I got a letter
where walt <wa1ter@myrealbox.com> said that...
> Now, when Micro$oft (eventually) takes its (natural)
> place as a loser in the marketplace -- who will the
> lawyers be anxious to sue?

Well, hopefully not git developers...? :-)

-- 
				Petr "Pasky the on-topic" Baudis
Stuff: http://pasky.or.cz/
Of the 3 great composers Mozart tells us what it's like to be human,
Beethoven tells us what it's like to be Beethoven and Bach tells us
what it's like to be the universe.  -- Douglas Adams

^ permalink raw reply

* Re: [Announce] gitview-0.1
From: Aneesh Kumar @ 2006-02-01 11:28 UTC (permalink / raw)
  To: Dave Jones; +Cc: git, junkio
In-Reply-To: <cc723f590601312041o1dc594c7t69418b735ef29ee@mail.gmail.com>

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

On 2/1/06, Aneesh Kumar <aneesh.kumar@gmail.com> wrote:
> On 2/1/06, Dave Jones <davej@redhat.com> wrote:

[..snip...]

> >
> > The one thing I like about gitk over this though is that with gitk
> > you don't have to click a button to see the diff.
> > For casual history browsing, it's much nicer to just scroll the bottom
> > window. Making it pop up a new window for each diff is just irritating.
> >
>
> I always browse the repository looking at the commit message and only
> if interested i look at the changes.  That's why i made it look that
> way.


with gitview  (attached) I have added an option --with-diff. That will
add the diff details. Now I would like somebody with good git
knowledge to review the different  commands that i am using to get
information out of git  For example with --with-diff option i am
getting the complete diff by

for parent_id in  parent_sha1 parent_sha2 parent_sha3 .......
commit_diff = commit_diff + git diff tree -p parent_id commit_sha1

is this the correct way ?

-aneesh

[-- Attachment #2: gitview --]
[-- Type: application/octet-stream, Size: 24940 bytes --]

#! /usr/bin/env python

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.

""" gitview
GUI browser for git repository 
This program is based on bzrk by Scott James Remnant <scott@ubuntu.com>
"""
__author__    = "Aneesh Kumar K.V <aneesh.kumar@gmail.com>"


import sys
import os
import gtk
import pygtk
import pango
import re
import time
import gobject
import cairo
import math
import string

try:
    import gtksourceview
    have_gtksourceview = True
except ImportError:
    have_gtksourceview = False
    print "Running without gtksourceview module"


def list_to_string(args, skip):
	count = len(args)
	i = skip
	str_arg=" "
	while (i < count ):
		str_arg = str_arg + args[i]
		str_arg = str_arg + " "
		i = i+1

	return str_arg


class CellRendererGraph(gtk.GenericCellRenderer):
	"""Cell renderer for directed graph.

	This module contains the implementation of a custom GtkCellRenderer that
	draws part of the directed graph based on the lines suggested by the code
	in graph.py.

	Because we're shiny, we use Cairo to do this, and because we're naughty
	we cheat and draw over the bits of the TreeViewColumn that are supposed to
	just be for the background.

	Properties:
	node              (column, colour) tuple to draw revision node,
	in_lines          (start, end, colour) tuple list to draw inward lines,
	out_lines         (start, end, colour) tuple list to draw outward lines.
	"""

	__gproperties__ = {
	"node":         ( gobject.TYPE_PYOBJECT, "node",
			  "revision node instruction",
			  gobject.PARAM_WRITABLE
			),
	"in-lines":     ( gobject.TYPE_PYOBJECT, "in-lines",
			  "instructions to draw lines into the cell",
			  gobject.PARAM_WRITABLE
			),
	"out-lines":    ( gobject.TYPE_PYOBJECT, "out-lines",
			  "instructions to draw lines out of the cell",
			  gobject.PARAM_WRITABLE
			),
	}

	def do_set_property(self, property, value):
		"""Set properties from GObject properties."""
		if property.name == "node":
			self.node = value
		elif property.name == "in-lines":
			self.in_lines = value
		elif property.name == "out-lines":
			self.out_lines = value
		else:
			raise AttributeError, "no such property: '%s'" % property.name

	def box_size(self, widget):
		"""Calculate box size based on widget's font.

		Cache this as it's probably expensive to get.  It ensures that we
		draw the graph at least as large as the text.
		"""
		try:
			return self._box_size
		except AttributeError:
			pango_ctx = widget.get_pango_context()
			font_desc = widget.get_style().font_desc
			metrics = pango_ctx.get_metrics(font_desc)

			ascent = pango.PIXELS(metrics.get_ascent())
			descent = pango.PIXELS(metrics.get_descent())

			self._box_size = ascent + descent + 6
			return self._box_size

	def set_colour(self, ctx, colour, bg, fg):
		"""Set the context source colour.

		Picks a distinct colour based on an internal wheel; the bg
		parameter provides the value that should be assigned to the 'zero'
		colours and the fg parameter provides the multiplier that should be
		applied to the foreground colours.
		"""
		colours = [
		    ( 1.0, 0.0, 0.0 ),
		    ( 1.0, 1.0, 0.0 ),
		    ( 0.0, 1.0, 0.0 ),
		    ( 0.0, 1.0, 1.0 ),
		    ( 0.0, 0.0, 1.0 ),
		    ( 1.0, 0.0, 1.0 ),
		    ]

		colour %= len(colours)
		red   = (colours[colour][0] * fg) or bg
		green = (colours[colour][1] * fg) or bg
		blue  = (colours[colour][2] * fg) or bg

		ctx.set_source_rgb(red, green, blue)

	def on_get_size(self, widget, cell_area):
		"""Return the size we need for this cell.

		Each cell is drawn individually and is only as wide as it needs
		to be, we let the TreeViewColumn take care of making them all
		line up.
		"""
		box_size = self.box_size(widget)

		cols = self.node[0]
		for start, end, colour in self.in_lines + self.out_lines:
			cols = max(cols, start, end)

		width = box_size * (cols + 1)
		height = box_size

		# FIXME I have no idea how to use cell_area properly
		return (0, 0, width, height)

	def on_render(self, window, widget, bg_area, cell_area, exp_area, flags):
		"""Render an individual cell.

		Draws the cell contents using cairo, taking care to clip what we
		do to within the background area so we don't draw over other cells.
		Note that we're a bit naughty there and should really be drawing
		in the cell_area (or even the exposed area), but we explicitly don't
		want any gutter.

		We try and be a little clever, if the line we need to draw is going
		to cross other columns we actually draw it as in the .---' style
		instead of a pure diagonal ... this reduces confusion by an
		incredible amount.
		"""
		ctx = window.cairo_create()
		ctx.rectangle(bg_area.x, bg_area.y, bg_area.width, bg_area.height)
		ctx.clip()

		box_size = self.box_size(widget)

		ctx.set_line_width(box_size / 8)
		ctx.set_line_cap(cairo.LINE_CAP_SQUARE)

		# Draw lines into the cell
		for start, end, colour in self.in_lines:
			ctx.move_to(cell_area.x + box_size * start + box_size / 2,
					bg_area.y - bg_area.height / 2)

			if start - end > 1:
				ctx.line_to(cell_area.x + box_size * start, bg_area.y)
				ctx.line_to(cell_area.x + box_size * end + box_size, bg_area.y)
			elif start - end < -1:
				ctx.line_to(cell_area.x + box_size * start + box_size,
						bg_area.y)
				ctx.line_to(cell_area.x + box_size * end, bg_area.y)

		    	ctx.line_to(cell_area.x + box_size * end + box_size / 2,
					bg_area.y + bg_area.height / 2)

			self.set_colour(ctx, colour, 0.0, 0.65)
			ctx.stroke()

		# Draw lines out of the cell
		for start, end, colour in self.out_lines:
			ctx.move_to(cell_area.x + box_size * start + box_size / 2,
					bg_area.y + bg_area.height / 2)

			if start - end > 1:
				ctx.line_to(cell_area.x + box_size * start,
						bg_area.y + bg_area.height)
				ctx.line_to(cell_area.x + box_size * end + box_size,
						bg_area.y + bg_area.height)
			elif start - end < -1:
				ctx.line_to(cell_area.x + box_size * start + box_size,
						bg_area.y + bg_area.height)
				ctx.line_to(cell_area.x + box_size * end,
						bg_area.y + bg_area.height)

			ctx.line_to(cell_area.x + box_size * end + box_size / 2,
					bg_area.y + bg_area.height / 2 + bg_area.height)

			self.set_colour(ctx, colour, 0.0, 0.65)
			ctx.stroke()

		# Draw the revision node in the right column
		(column, colour) = self.node
		ctx.arc(cell_area.x + box_size * column + box_size / 2,
				cell_area.y + cell_area.height / 2,
				box_size / 4, 0, 2 * math.pi)

		self.set_colour(ctx, colour, 0.0, 0.5)
		ctx.stroke_preserve()

		self.set_colour(ctx, colour, 0.5, 1.0)
		ctx.fill()

class Commit:
	""" This represent a commit object obtained after parsing the git-rev-list 
	output """

	def __init__(self):
		self.message 		= ""
		self.author		= ""
		self.date 		= ""
		self.committer 		= ""
		self.commit_date 	= ""
		self.commit_sha1	= ""
		self.parent_sha1	= [ ]

	def get_message(self, with_diff=0):
		fp = os.popen("git cat-file commit " + self.commit_sha1)
		message = fp.read()
		fp.close()

		if (with_diff == 1):
				message = message + self.diff_tree()
		return message

	def diff_tree(self):
		# newline to seperate the commit message and the diff
		diff = "\n\n"
		for parent_id in self.parent_sha1:
			if (parent_id == 0):
				return ""
			fp = os.popen("git diff-tree -p " + parent_id + " " + self.commit_sha1)
			diff = diff + fp.read()
			fp.close()

		return diff

class DiffWindow:
	"""Diff window.
	This object represents and manages a single window containing the
	differences between two revisions on a branch.
	"""

	def __init__(self):
		self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
		self.window.set_border_width(0)
		self.window.set_title("Git repository browser diff window")

		# Use two thirds of the screen by default
		screen = self.window.get_screen()
		monitor = screen.get_monitor_geometry(0)
		width = int(monitor.width * 0.66)
		height = int(monitor.height * 0.66)
		self.window.set_default_size(width, height)

		self.construct()

	def construct(self):
		"""Construct the window contents."""
		hbox = gtk.HBox(spacing=6)
		hbox.set_border_width(12)
		self.window.add(hbox)
		hbox.show()

		scrollwin = gtk.ScrolledWindow()
		scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
		scrollwin.set_shadow_type(gtk.SHADOW_IN)
		hbox.pack_start(scrollwin, expand=True, fill=True)
		scrollwin.show()

		if have_gtksourceview:
			self.buffer = gtksourceview.SourceBuffer()
			slm = gtksourceview.SourceLanguagesManager()
			gsl = slm.get_language_from_mime_type("text/x-patch")
			self.buffer.set_highlight(True)
			self.buffer.set_language(gsl)
			sourceview = gtksourceview.SourceView(self.buffer)
		else:
			self.buffer = gtk.TextBuffer()
			sourceview = gtk.TextView(self.buffer)

		sourceview.set_editable(False)
		sourceview.modify_font(pango.FontDescription("Monospace"))
		scrollwin.add(sourceview)
		sourceview.show()


	def set_diff(self, commit_sha1, parent_sha1):
		"""Set the differences showed by this window.
		Compares the two trees and populates the window with the
		differences.
		"""
		# Diff with the first commit or the last commit shows nothing
		if (commit_sha1 == 0 or parent_sha1 == 0 ):
			return 

		fp = os.popen("git diff-tree -p " + parent_sha1 + " " + commit_sha1)
		self.buffer.set_text(fp.read())
		fp.close()
		self.window.show()


class GitView:
	""" This is the main class
	"""

	def __init__(self, with_diff=0):
		self.with_diff = with_diff
        	self.window =  	gtk.Window(gtk.WINDOW_TOPLEVEL)
        	self.window.set_border_width(0)
		self.window.set_title("Git repository browser")


        	# Use three-quarters of the screen by default
        	screen = self.window.get_screen()
        	monitor = screen.get_monitor_geometry(0)
        	width = int(monitor.width * 0.75)
        	height = int(monitor.height * 0.75)
        	self.window.set_default_size(width, height)

        	# FIXME AndyFitz!
        	icon = self.window.render_icon(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)
        	self.window.set_icon(icon)

        	self.accel_group = gtk.AccelGroup()
        	self.window.add_accel_group(self.accel_group)

        	self.construct()


	def construct(self):
		"""Construct the window contents."""
		paned = gtk.VPaned()
		paned.pack1(self.construct_top(), resize=True, shrink=False)
		paned.pack2(self.construct_bottom(), resize=False, shrink=True)
		self.window.add(paned)
		paned.show()

	def construct_top(self):
		"""Construct the top-half of the window."""
		vbox = gtk.VBox(spacing=6)
		vbox.set_border_width(12)
		vbox.show()

		scrollwin = gtk.ScrolledWindow()
		scrollwin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
		scrollwin.set_shadow_type(gtk.SHADOW_IN)
		vbox.pack_start(scrollwin, expand=True, fill=True)
		scrollwin.show()

		self.treeview = gtk.TreeView()
		self.treeview.set_rules_hint(True)
		self.treeview.set_search_column(4)
		self.treeview.connect("cursor-changed", self._treeview_cursor_cb)
		scrollwin.add(self.treeview)
		self.treeview.show()

		cell = CellRendererGraph()
		column = gtk.TreeViewColumn()
		column.set_resizable(False)
		column.pack_start(cell, expand=False)
		column.add_attribute(cell, "node", 1)
		column.add_attribute(cell, "in-lines", 2)
		column.add_attribute(cell, "out-lines", 3)
		self.treeview.append_column(column)

		cell = gtk.CellRendererText()
		cell.set_property("width-chars", 65)
		cell.set_property("ellipsize", pango.ELLIPSIZE_END)
		column = gtk.TreeViewColumn("Message")
		column.set_resizable(True)
		column.pack_start(cell, expand=True)
		column.add_attribute(cell, "text", 4)
		self.treeview.append_column(column)

		cell = gtk.CellRendererText()
		cell.set_property("width-chars", 40)
		cell.set_property("ellipsize", pango.ELLIPSIZE_END)
		column = gtk.TreeViewColumn("Author")
		column.set_resizable(True)
		column.pack_start(cell, expand=True)
		column.add_attribute(cell, "text", 5)
		self.treeview.append_column(column)

		cell = gtk.CellRendererText()
		cell.set_property("ellipsize", pango.ELLIPSIZE_END)
		column = gtk.TreeViewColumn("Date")
		column.set_resizable(True)
		column.pack_start(cell, expand=True)
		column.add_attribute(cell, "text", 6)
		self.treeview.append_column(column)

		return vbox


	def construct_bottom(self):
		"""Construct the bottom half of the window."""
		vbox = gtk.VBox(False, spacing=6)
		vbox.set_border_width(12)
		(width, height) = self.window.get_size()
		vbox.set_size_request(width, int(height / 2.5))
		vbox.show()

		self.table = gtk.Table(rows=4, columns=4)
		self.table.set_row_spacings(6)
		self.table.set_col_spacings(6)
		vbox.pack_start(self.table, expand=False, fill=True)
		self.table.show()

		align = gtk.Alignment(0.0, 0.5)
		label = gtk.Label()
		label.set_markup("<b>Revision:</b>")
		align.add(label)
		self.table.attach(align, 0, 1, 0, 1, gtk.FILL, gtk.FILL)
		label.show()
		align.show()

		align = gtk.Alignment(0.0, 0.5)
		self.revid_label = gtk.Label()
		self.revid_label.set_selectable(True)
		align.add(self.revid_label)
		self.table.attach(align, 1, 2, 0, 1, gtk.EXPAND | gtk.FILL, gtk.FILL)
		self.revid_label.show()
		align.show()

		align = gtk.Alignment(0.0, 0.5)
		label = gtk.Label()
		label.set_markup("<b>Committer:</b>")
		align.add(label)
		self.table.attach(align, 0, 1, 1, 2, gtk.FILL, gtk.FILL)
		label.show()
		align.show()

		align = gtk.Alignment(0.0, 0.5)
		self.committer_label = gtk.Label()
		self.committer_label.set_selectable(True)
		align.add(self.committer_label)
		self.table.attach(align, 1, 2, 1, 2, gtk.EXPAND | gtk.FILL, gtk.FILL)
		self.committer_label.show()
		align.show()

		align = gtk.Alignment(0.0, 0.5)
		label = gtk.Label()
		label.set_markup("<b>Timestamp:</b>")
		align.add(label)
		self.table.attach(align, 0, 1, 2, 3, gtk.FILL, gtk.FILL)
		label.show()
		align.show()

		align = gtk.Alignment(0.0, 0.5)
		self.timestamp_label = gtk.Label()
		self.timestamp_label.set_selectable(True)
		align.add(self.timestamp_label)
		self.table.attach(align, 1, 2, 2, 3, gtk.EXPAND | gtk.FILL, gtk.FILL)
		self.timestamp_label.show()
		align.show()

		align = gtk.Alignment(0.0, 0.5)
		label = gtk.Label()
		label.set_markup("<b>Parents:</b>")
		align.add(label)
		self.table.attach(align, 0, 1, 3, 4, gtk.FILL, gtk.FILL)
		label.show()
		align.show()
		self.parents_widgets = []

		align = gtk.Alignment(0.0, 0.5)
		label = gtk.Label()
		label.set_markup("<b>Children:</b>")
		align.add(label)
		self.table.attach(align, 2, 3, 3, 4, gtk.FILL, gtk.FILL)
		label.show()
		align.show()
		self.children_widgets = []

		scrollwin = gtk.ScrolledWindow()
		scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
		scrollwin.set_shadow_type(gtk.SHADOW_IN)
		vbox.pack_start(scrollwin, expand=True, fill=True)
		scrollwin.show()

		if have_gtksourceview:
			self.message_buffer = gtksourceview.SourceBuffer()
			slm = gtksourceview.SourceLanguagesManager()
			gsl = slm.get_language_from_mime_type("text/x-patch")
			self.message_buffer.set_highlight(True)
			self.message_buffer.set_language(gsl)
			sourceview = gtksourceview.SourceView(self.message_buffer)
		else:
			self.message_buffer = gtk.TextBuffer()
			sourceview = gtk.TextView(self.message_buffer)

		sourceview.set_editable(False)
		sourceview.modify_font(pango.FontDescription("Monospace"))
		scrollwin.add(sourceview)
		sourceview.show()

		return vbox

	def _treeview_cursor_cb(self, *args):
		"""Callback for when the treeview cursor changes."""
		(path, col) = self.treeview.get_cursor()
		commit = self.model[path][0]

		if commit.committer is not None:
			committer = commit.committer
			timestamp = commit.commit_date
			message   =  commit.get_message(self.with_diff)
			revid_label = commit.commit_sha1
		else:
    			committer = ""
    			timestamp = ""
    			message = ""
			revid_label = ""

		self.revid_label.set_text(revid_label)
		self.committer_label.set_text(committer)
		self.timestamp_label.set_text(timestamp)
		self.message_buffer.set_text(message)

		for widget in self.parents_widgets:
			self.table.remove(widget)

		self.parents_widgets = []
		self.table.resize(4 + len(commit.parent_sha1) - 1, 4)
		for idx, parent_id in enumerate(commit.parent_sha1):
			self.table.set_row_spacing(idx + 3, 0)

			align = gtk.Alignment(0.0, 0.0)
			self.parents_widgets.append(align)
			self.table.attach(align, 1, 2, idx + 3, idx + 4,
					gtk.EXPAND | gtk.FILL, gtk.FILL)
			align.show()

			hbox = gtk.HBox(False, 0)
			align.add(hbox)
			hbox.show()

			label = gtk.Label(parent_id)
			label.set_selectable(True)
			hbox.pack_start(label, expand=False, fill=True)
			label.show()

			image = gtk.Image()
			image.set_from_stock(gtk.STOCK_JUMP_TO, gtk.ICON_SIZE_MENU)
			image.show()

			button = gtk.Button()
			button.add(image)
			button.set_relief(gtk.RELIEF_NONE)
			button.connect("clicked", self._go_clicked_cb, parent_id)
			hbox.pack_start(button, expand=False, fill=True)
			button.show()

			image = gtk.Image()
			image.set_from_stock(gtk.STOCK_FIND, gtk.ICON_SIZE_MENU)
			image.show()

			button = gtk.Button()
			button.add(image)
			button.set_relief(gtk.RELIEF_NONE)
			button.set_sensitive(True)
			button.connect("clicked", self._show_clicked_cb,
					commit.commit_sha1, parent_id)
			hbox.pack_start(button, expand=False, fill=True)
			button.show()

		# Populate with child details
		for widget in self.children_widgets:
			self.table.remove(widget)

		self.children_widgets = []
		try:
			child_sha1 = self.children_sha1[commit.commit_sha1]
		except KeyError:
			# We don't have child
			child_sha1 = [ 0 ]

		if ( len(child_sha1) > len(commit.parent_sha1)):
			self.table.resize(4 + len(child_sha1) - 1, 4)

		for idx, child_id in enumerate(child_sha1):
			self.table.set_row_spacing(idx + 3, 0)

			align = gtk.Alignment(0.0, 0.0)
			self.children_widgets.append(align)
			self.table.attach(align, 3, 4, idx + 3, idx + 4,
					gtk.EXPAND | gtk.FILL, gtk.FILL)
			align.show()

			hbox = gtk.HBox(False, 0)
			align.add(hbox)
			hbox.show()

			label = gtk.Label(child_id)
			label.set_selectable(True)
			hbox.pack_start(label, expand=False, fill=True)
			label.show()

			image = gtk.Image()
			image.set_from_stock(gtk.STOCK_JUMP_TO, gtk.ICON_SIZE_MENU)
			image.show()

			button = gtk.Button()
			button.add(image)
			button.set_relief(gtk.RELIEF_NONE)
			button.connect("clicked", self._go_clicked_cb, child_id)
			hbox.pack_start(button, expand=False, fill=True)
			button.show()

			image = gtk.Image()
			image.set_from_stock(gtk.STOCK_FIND, gtk.ICON_SIZE_MENU)
			image.show()

			button = gtk.Button()
			button.add(image)
			button.set_relief(gtk.RELIEF_NONE)
			button.set_sensitive(True)
			button.connect("clicked", self._show_clicked_cb,
					child_id, commit.commit_sha1)
			hbox.pack_start(button, expand=False, fill=True)
			button.show()

	def _destroy_cb(self, widget):
		"""Callback for when a window we manage is destroyed."""
		self.quit()


	def quit(self):
		"""Stop the GTK+ main loop."""
		gtk.main_quit()

	def run(self, args):
		self.set_branch(args)
        	self.window.connect("destroy", self._destroy_cb)
		self.window.show()
		gtk.main()

	def set_branch(self, args):
		"""Fill in different windows with info from the reposiroty"""
		fp = os.popen("git rev-parse --sq --default HEAD " + list_to_string(args, 1))
		git_rev_list_cmd = fp.read()
		fp.close()
		fp = os.popen("git rev-list  --header --topo-order --parents " + git_rev_list_cmd)
		self.update_window(fp)

	def update_window(self, fp):
		commit_lines = []

		self.model = gtk.ListStore(gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT,
				gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT, str, str, str)

		# used for cursor positioning 
		self.index = {}

		self.colours = {}
		self.nodepos = {}
		self.incomplete_line = {}
		self.children_sha1 = {}

		index = 0
		last_colour = 0
		last_nodepos = -1
		out_line = []	
		input_line = fp.readline()
		while (input_line != ""):
			# The commit header ends with '\0'
			# This NULL is immediately followed by the sha1 of the 
			# next commit
			if (input_line[0] != '\0'):
				commit_lines.append(input_line)
				input_line = fp.readline()
				continue;

			commit = self.parse_commit(commit_lines);
			if (commit != None ):
				(out_line, last_colour, last_nodepos) = self.draw_graph(commit,
										index, out_line,
										last_colour,
										last_nodepos)
				self.index[commit.commit_sha1] = index
				index += 1

			# Skip the '\0
			commit_lines = []
			commit_lines.append(input_line[1:])
			input_line = fp.readline()

		fp.close()

		self.treeview.set_model(self.model)
		self.treeview.show()

	def draw_graph(self, commit, index, out_line, last_colour, last_nodepos):
		in_line=[]

		#   |   -> outline
		#   X
		#   |\  <- inline 

		# Reset nodepostion
		if (last_nodepos > 5):
			last_nodepos = 0

		# Add the incomplete lines of the last cell in this 
		for sha1 in self.incomplete_line.keys():
			if ( sha1 != commit.commit_sha1):
				for pos in self.incomplete_line[sha1]:
					in_line.append((pos, pos, self.colours[sha1]))
			else:
				del self.incomplete_line[sha1]

		try:
			colour = self.colours[commit.commit_sha1]
		except KeyError:
			last_colour +=1
			self.colours[commit.commit_sha1] = last_colour
			colour =  last_colour
		try:
			node_pos = self.nodepos[commit.commit_sha1]
		except KeyError:
			last_nodepos +=1
			self.nodepos[commit.commit_sha1] = last_nodepos
			node_pos = last_nodepos

		#The first parent always continue on the same line
		try:
			# check we alreay have the value
			tmp_node_pos = self.nodepos[commit.parent_sha1[0]]
		except KeyError:
			self.colours[commit.parent_sha1[0]] = colour
			self.nodepos[commit.parent_sha1[0]] = node_pos

		in_line.append((node_pos, self.nodepos[commit.parent_sha1[0]],
					self.colours[commit.parent_sha1[0]]))

		self.add_incomplete_line(commit.parent_sha1[0], index+1)

		if (len(commit.parent_sha1) > 1):
			for parent_id in commit.parent_sha1[1:]:
				try:
					tmp_node_pos = self.nodepos[parent_id]
				except KeyError:
					last_colour += 1;
					self.colours[parent_id] = last_colour
					last_nodepos +=1
					self.nodepos[parent_id] = last_nodepos

				in_line.append((node_pos, self.nodepos[parent_id],
							self.colours[parent_id]))
				self.add_incomplete_line(parent_id, index+1)

		node = (node_pos, colour) 

		self.model.append([commit, node, out_line, in_line,
				commit.message, commit.author, commit.date]) 

		return (in_line, last_colour, last_nodepos)

	def add_incomplete_line(self, sha1, index):
		try:
			self.incomplete_line[sha1].append(self.nodepos[sha1])
		except KeyError:
			self.incomplete_line[sha1] = [self.nodepos[sha1]]


	def parse_commit(self, commit_lines):

		commit = Commit()

		# First line is the sha1 lines
		line = string.strip(commit_lines[0])
		sha1 = re.split(" ", line)
		commit.commit_sha1 = sha1[0]
		commit.parent_sha1 = sha1[1:]

		#build the child list
		for parent_id in commit.parent_sha1:
			try:
				self.children_sha1[parent_id].append(commit.commit_sha1)
			except KeyError:
				self.children_sha1[parent_id] = [commit.commit_sha1]

		# IF we don't have parent
		if (len(commit.parent_sha1) == 0):
			commit.parent_sha1 = [0]

		for line in commit_lines[1:]:
			m = re.match("^ ", line)
			if (m != None):
				# First line of the commit message used for short log
				if commit.message == "":
					commit.message = string.strip(line)
				continue

			m = re.match("tree", line)
			if (m != None):
				continue

			m = re.match("parent", line)
			if (m != None):
				continue


			m = re.match("author", line)
			if (m  != None ):
				patch_author = re.split(" ", line)	
				commit.author = list_to_string(patch_author[1:-2], 0)
				commit.date = time.strftime("%Y-%m-%d %H:%M:%S",
						time.gmtime(float(patch_author[-2])))
				continue

			m = re.match("committer", line)
			if (m  != None ):
				patch_committer = re.split(" ", line)	
				commit.committer = list_to_string(patch_committer[1:-2], 0)
				commit.commit_date = time.strftime("%Y-%m-%d %H:%M:%S",
						time.gmtime(float(patch_committer[-2])))
				continue

		return commit


	def _go_clicked_cb(self, widget, revid):
		"""Callback for when the go button for a parent is clicked."""
		try:
			self.treeview.set_cursor(self.index[revid])
		except KeyError:
			print "Revision %s not present in the list" % revid
			# revid == 0 is the parent of the first commit
			if (revid != 0 ):
				print "Try running gitview without any options"

		self.treeview.grab_focus()

	def _show_clicked_cb(self, widget,  commit_sha1, parent_sha1):
		"""Callback for when the show button for a parent is clicked."""
		window = DiffWindow()
		window.set_diff(commit_sha1, parent_sha1)
		self.treeview.grab_focus()

if __name__ == "__main__":
	with_diff = 0

	if (len(sys.argv) > 1 ):
		if (sys.argv[1] == "--with-diff"):
			with_diff = 1

	view = GitView(with_diff)
	view.run(sys.argv[with_diff:])



^ permalink raw reply

* How to use git for Linux kernel development ?
From: Laurent Pinchart @ 2006-02-01 11:38 UTC (permalink / raw)
  To: git

Hi everybody,

my company decided to port the Linux kernel to custom hardware. As we need a 
repository for the sources, I thought I would give git a try. The man pages 
and tutorials are quite helpful to understand the git commands, but none of 
the documentation I found has been able to answer my (not so simple) 
question : given my use cases, how do I use git ?

Here are the use cases.

I need to port the Linux kernel to custom hardware. This means fixing bugs in 
the kernel, adding new features and new device drivers. Some of the 
modifications I make (bug fixes) will be pushed upstream by myself, some 
others by other developers (for instance I will apply patches found on 
sourceforge to support our non-UHCI/OHCI USB host controller, and I don't 
want to push code for which I'm not the maintainer upstream), and some device 
drivers will not be pushed at all.

I will base my work on the Linux kernel 2.6.15, which is the most recent 
release that has the features I need (2.6.16-rc1 broke serial port support on 
Freescale PowerQuickII processors). I plan to stay up-to-date with the main 
kernel repository by either fixing the problems in future releases or waiting 
for someone to fix them, depending on how much time I can spend on the 
project.

I have no idea how to layout my git repository to work on day-to-day 
development. I need to :

- commit bug fixes, external patches and internal modifications to a branch 
(or possibly on separate branches depending on what I commit if needed). The 
work will be based on Linux kernel 2.6.15 but I'd like to stay up-to-date 
with the master repository as much as possible.
- push bug fixes upstream by creating a patchset and submitting it by email.
- pull changes from upstream and merge them in my various branches when the 
upstream versions become stable enough.
- keep branches for all the versions shipped to the customers for bug fixes.

I'm the only developer working on the Linux kernel in my company, but that 
might change in a few months, so other developers will need to use git as 
well.

Is git able to accomodate my needs ? I've been trying to setup a git 
repository with a few branches over the last two days, but I always had to 
throw everything away and start back from zero. I haven't been able to figure 
out which branches I should create and how I should use them.

Thanks in advance for your help, and I hope I'm not asking too much. If really 
unable to use git, I'll go for SVN (or SourceSafe as that's what my company 
used until today, but I'd like to avoid it) even if I feel it will make 
keeping up-to-date with upstream more difficult.

Laurent Pinchart

^ permalink raw reply

* [PATCH 0/9] http-fetch fixes
From: Mark Wooding @ 2006-02-01 11:28 UTC (permalink / raw)
  To: git

This patch series fixes the git-http-fetch bug I reported in
`git-http-fetch failure/segfault -- alas no patch'.  The preliminary
patch I was working on /did/ actually fix the bug I found, but uncovered
a bunch more, which I think I've finally got to the bottom of.

The series applies to commit 1506fc34f7585880aeeb12b5fdfe2de4800f9df5.

Particularly: 

  1. watch-slot -- my fix for the actual bug.  Different from Nick
     Hangeval's separate-status structure, it works by locking slots
     that people still want answers from.  I've turned the in_use slot
     field into a bit mask and added a reference counter.  I think you
     can use Nick's patch instead if you prefer it.

  2, 3. list-corruption, abort-object-request -- fix bugs uncovered by
     watch-slot.  See the patch emails for the details.

  4, 5, 6, 7. multi-fdset, fix-rename-message, curl-verbose,
     par-control-flow-tidy -- various other relatively trivial changes I
     noticed while doing the main work above.  fix-rename-message is
     actually a bug, but if you think some other fix is better, that's
     cool; the others are cosmetic things.

  8, 9.  sanity-check-active-slots, sanity-check-object-queue --
     debugging code I threw in while I was trying to track down the main
     bugs.  I'm not going to cry if these aren't accepted (indeed, I
     separated them out and put them at the end specifically!) but they
     might prove useful to someone, so I thought I'd send 'em along
     anyway.

-- [mdw]

^ permalink raw reply

* [PATCH 3/9] http-fetch: Abort requests for objects which arrived in packs
From: Mark Wooding @ 2006-02-01 11:44 UTC (permalink / raw)
  To: git
In-Reply-To: <20060201112822.5042.41256.stgit@metalzone.distorted.org.uk>

From: Mark Wooding <mdw@distorted.org.uk>

In fetch_object, there's a call to release an object request if the
object mysteriously arrived, say in a pack.  Unfortunately, the fetch
attempt for this object might already be in progress, and we'll leak the
descriptor.  Instead, try to tidy away the request.

Signed-off-by: Mark Wooding <mdw@distorted.org.uk>
---

 http-fetch.c |   16 +++++++++++++++-
 http.c       |   20 ++++++++++++++++++--
 http.h       |    1 +
 3 files changed, 34 insertions(+), 3 deletions(-)

diff --git a/http-fetch.c b/http-fetch.c
index b1ee836..8656070 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -770,6 +770,20 @@ static int fetch_pack(struct alt_base *r
 	return 0;
 }
 
+static void abort_object_request(struct object_request *obj_req)
+{
+	if (obj_req->local >= 0) {
+		close(obj_req->local);
+		obj_req->local = -1;
+	}
+	unlink(obj_req->tmpfile);
+	if (obj_req->slot) {
+ 		release_active_slot(obj_req->slot);
+		obj_req->slot = NULL;
+	}
+	release_object_request(obj_req);
+}
+
 static int fetch_object(struct alt_base *repo, unsigned char *sha1)
 {
 	char *hex = sha1_to_hex(sha1);
@@ -782,7 +796,7 @@ static int fetch_object(struct alt_base 
 		return error("Couldn't find request for %s in the queue", hex);
 
 	if (has_sha1_file(obj_req->sha1)) {
-		release_object_request(obj_req);
+		abort_object_request(obj_req);
 		return 0;
 	}
 
diff --git a/http.c b/http.c
index 498b4ae..0a70e1c 100644
--- a/http.c
+++ b/http.c
@@ -438,11 +438,27 @@ void run_active_slot(struct active_reque
 #endif
 }
 
-static void finish_active_slot(struct active_request_slot *slot)
+static void closedown_active_slot(struct active_request_slot *slot)
 {
-	slot->in_use &= ~SLOTUSE_ACTIVE;
+        slot->in_use &= ~SLOTUSE_ACTIVE;
 	if (!slot->in_use)
 		active_requests--;
+}
+
+void release_active_slot(struct active_request_slot *slot)
+{
+	closedown_active_slot(slot);
+	if (slot->curl) {
+		curl_multi_remove_handle(curlm, slot->curl);
+		curl_easy_cleanup(slot->curl);
+		slot->curl = NULL;
+	}
+	fill_active_slots();
+}
+
+static void finish_active_slot(struct active_request_slot *slot)
+{
+	closedown_active_slot(slot);
         curl_easy_getinfo(slot->curl, CURLINFO_HTTP_CODE, &slot->http_code);
  
         /* Run callback if appropriate */
diff --git a/http.h b/http.h
index a66e06a..d028a5d 100644
--- a/http.h
+++ b/http.h
@@ -58,6 +58,7 @@ extern struct active_request_slot *get_a
 extern int start_active_slot(struct active_request_slot *slot);
 extern void run_active_slot(struct active_request_slot *slot);
 extern void finish_all_active_slots(void);
+extern void release_active_slot(struct active_request_slot *slot);
 
 #ifdef USE_CURL_MULTI
 extern void fill_active_slots(void);

^ permalink raw reply related

* [PATCH 2/9] http-fetch: Fix object list corruption in fill_active_slots().
From: Mark Wooding @ 2006-02-01 11:44 UTC (permalink / raw)
  To: git
In-Reply-To: <20060201112822.5042.41256.stgit@metalzone.distorted.org.uk>

From: Mark Wooding <mdw@distorted.org.uk>

In fill_active_slots() -- if we find an object which has already arrived,
say as part of a pack, /don't/ remove it from the list.  It's already been
prefetched and someone will ask for it later.  Just label it as done and
carry blithely on.  (As it was, the code would dereference a freed object
to continue through the list anyway.)

Signed-off-by: Mark Wooding <mdw@distorted.org.uk>
---

 http-fetch.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/http-fetch.c b/http-fetch.c
index 4b1b62d..b1ee836 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -312,7 +312,7 @@ void fill_active_slots(void)
 	while (active_requests < max_requests && obj_req != NULL) {
 		if (obj_req->state == WAITING) {
 			if (has_sha1_file(obj_req->sha1))
-				release_object_request(obj_req);
+				obj_req->state = COMPLETE;
 			else
 				start_object_request(obj_req);
 			curl_multi_perform(curlm, &num_transfers);

^ permalink raw reply related

* [PATCH 1/9] http-fetch: Mark slots as `watched' to stop them being reused.
From: Mark Wooding @ 2006-02-01 11:44 UTC (permalink / raw)
  To: git
In-Reply-To: <20060201112822.5042.41256.stgit@metalzone.distorted.org.uk>

From: Mark Wooding <mdw@distorted.org.uk>

Previously, it was possible for the slot passed to run_active_slot() to
be reused before that function returned, rendering its completion status
useless.  This is particularly noticeable when fetching a repository
with a wide, flat directory structure (lots of blobs in the top-level
tree), a single unpacked commit which changes just one or two blobs, and
the rest of the history in one pack.  When fetch_indices() gets the
pack-list, the slot is (usually!) reused for some blob object which is
packed, so it fails with 404; fetch_indices() decides that the pack-list
fetch failed, and nothing therefore fetches the pack.

We fix this by adding a feature to lock slots against reuse.

Signed-off-by: Mark Wooding <mdw@distorted.org.uk>
---

 http-fetch.c |    6 +++---
 http.c       |   43 +++++++++++++++++++++++++++++++++----------
 http.h       |    6 +++++-
 3 files changed, 41 insertions(+), 14 deletions(-)

diff --git a/http-fetch.c b/http-fetch.c
index 61b2188..4b1b62d 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -465,13 +465,13 @@ static void process_alternates_response(
 				base);
 			curl_easy_setopt(slot->curl, CURLOPT_URL,
 					 alt_req->url);
-			active_requests++;
-			slot->in_use = 1;
+			if (!slot->in_use)
+				active_requests++;
+			slot->in_use |= SLOTUSE_ACTIVE;
 			if (start_active_slot(slot)) {
 				return;
 			} else {
 				got_alternates = -1;
-				slot->in_use = 0;
 				return;
 			}
 		}
diff --git a/http.c b/http.c
index 75e6717..498b4ae 100644
--- a/http.c
+++ b/http.c
@@ -262,7 +262,7 @@ void http_cleanup(void)
 
 	while (slot != NULL) {
 #ifdef USE_CURL_MULTI
-		if (slot->in_use) {
+		if (slot->in_use & SLOTUSE_ACTIVE) {
 			curl_easy_getinfo(slot->curl,
 					  CURLINFO_EFFECTIVE_URL,
 					  &wait_url);
@@ -311,6 +311,7 @@ struct active_request_slot *get_active_s
 		newslot->curl = NULL;
 		newslot->in_use = 0;
 		newslot->next = NULL;
+		newslot->nrefs = 0;
 
 		slot = active_queue_head;
 		if (slot == NULL) {
@@ -333,7 +334,7 @@ struct active_request_slot *get_active_s
 	}
 
 	active_requests++;
-	slot->in_use = 1;
+	slot->in_use |= SLOTUSE_ACTIVE;
 	slot->local = NULL;
 	slot->callback_data = NULL;
 	slot->callback_func = NULL;
@@ -351,8 +352,9 @@ int start_active_slot(struct active_requ
 
 	if (curlm_result != CURLM_OK &&
 	    curlm_result != CURLM_CALL_MULTI_PERFORM) {
-		active_requests--;
-		slot->in_use = 0;
+		slot->in_use &= ~SLOTUSE_ACTIVE;
+		if (!slot->in_use)
+			active_requests--;
 		return 0;
 	}
 #endif
@@ -375,6 +377,24 @@ void step_active_slots(void)
 }
 #endif
 
+static void watch_active_slot(struct active_request_slot *slot)
+{
+	if (!slot->in_use)
+		active_requests++;
+	slot->in_use |= SLOTUSE_REF;
+	slot->nrefs++;
+}
+
+static void unwatch_active_slot(struct active_request_slot *slot)
+{
+	slot->nrefs--;
+	if (!slot->nrefs) {
+		slot->in_use &= ~SLOTUSE_REF;
+		if (!slot->in_use)
+			active_requests--;
+	}
+}
+
 void run_active_slot(struct active_request_slot *slot)
 {
 #ifdef USE_CURL_MULTI
@@ -386,7 +406,8 @@ void run_active_slot(struct active_reque
 	int max_fd;
 	struct timeval select_timeout;
 
-	while (slot->in_use) {
+	watch_active_slot(slot);
+	while (slot->in_use & SLOTUSE_ACTIVE) {
 		data_received = 0;
 		step_active_slots();
 
@@ -397,7 +418,7 @@ void run_active_slot(struct active_reque
 			last_pos = current_pos;
 		}
 
-		if (slot->in_use && !data_received) {
+		if ((slot->in_use & SLOTUSE_ACTIVE) && !data_received) {
 			max_fd = 0;
 			FD_ZERO(&readfds);
 			FD_ZERO(&writefds);
@@ -408,8 +429,9 @@ void run_active_slot(struct active_reque
 			       &excfds, &select_timeout);
 		}
 	}
+	unwatch_active_slot(slot);
 #else
-	while (slot->in_use) {
+	while (slot->in_use & SLOTUSE_ACTIVE) {
 		slot->curl_result = curl_easy_perform(slot->curl);
 		finish_active_slot(slot);
 	}
@@ -418,8 +440,9 @@ void run_active_slot(struct active_reque
 
 static void finish_active_slot(struct active_request_slot *slot)
 {
-        active_requests--;
-        slot->in_use = 0;
+	slot->in_use &= ~SLOTUSE_ACTIVE;
+	if (!slot->in_use)
+		active_requests--;
         curl_easy_getinfo(slot->curl, CURLINFO_HTTP_CODE, &slot->http_code);
  
         /* Run callback if appropriate */
@@ -433,7 +456,7 @@ void finish_all_active_slots(void)
 	struct active_request_slot *slot = active_queue_head;
 
 	while (slot != NULL)
-		if (slot->in_use) {
+		if (slot->in_use & SLOTUSE_ACTIVE) {
 			run_active_slot(slot);
 			slot = active_queue_head;
 		} else {
diff --git a/http.h b/http.h
index ed4ea33..a66e06a 100644
--- a/http.h
+++ b/http.h
@@ -26,7 +26,8 @@ struct active_request_slot
 {
 	CURL *curl;
 	FILE *local;
-	int in_use;
+	unsigned in_use;
+	int nrefs;
 	CURLcode curl_result;
 	long http_code;
 	void *callback_data;
@@ -34,6 +35,9 @@ struct active_request_slot
 	struct active_request_slot *next;
 };
 
+#define SLOTUSE_ACTIVE 1u
+#define SLOTUSE_REF 2u
+
 struct buffer
 {
         size_t posn;

^ permalink raw reply related

* [PATCH 4/9] http-fetch: Actually watch the file descriptors of interest.
From: Mark Wooding @ 2006-02-01 11:44 UTC (permalink / raw)
  To: git
In-Reply-To: <20060201112822.5042.41256.stgit@metalzone.distorted.org.uk>

From: Mark Wooding <mdw@distorted.org.uk>

Presumably this was just some kind of oversight.

Signed-off-by: Mark Wooding <mdw@distorted.org.uk>
---

 http.c |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/http.c b/http.c
index 0a70e1c..521323c 100644
--- a/http.c
+++ b/http.c
@@ -425,6 +425,8 @@ void run_active_slot(struct active_reque
 			FD_ZERO(&excfds);
 			select_timeout.tv_sec = 0;
 			select_timeout.tv_usec = 50000;
+			curl_multi_fdset(curlm, &readfds, &writefds,
+					 &excfds, &max_fd);
 			select(max_fd, &readfds, &writefds,
 			       &excfds, &select_timeout);
 		}

^ permalink raw reply related

* [PATCH 5/9] http-fetch: Fix message reporting rename of object file.
From: Mark Wooding @ 2006-02-01 11:44 UTC (permalink / raw)
  To: git
In-Reply-To: <20060201112822.5042.41256.stgit@metalzone.distorted.org.uk>

From: Mark Wooding <mdw@distorted.org.uk>

move_temp_to_file returns 0 or -1.  This is not a good thing to pass to
strerror(3).  Fortunately, someone already reported the error, so don't
worry too much.

Signed-off-by: Mark Wooding <mdw@distorted.org.uk>
---

 http-fetch.c |    5 ++---
 1 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/http-fetch.c b/http-fetch.c
index 8656070..f1aac14 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -831,9 +831,8 @@ static int fetch_object(struct alt_base 
 	} else if (memcmp(obj_req->sha1, obj_req->real_sha1, 20)) {
 		ret = error("File %s has bad hash\n", hex);
 	} else if (obj_req->rename < 0) {
-		ret = error("unable to write sha1 filename %s: %s",
-			    obj_req->filename,
-			    strerror(obj_req->rename));
+		ret = error("unable to write sha1 filename %s",
+			    obj_req->filename);
 	}
 
 	release_object_request(obj_req);

^ permalink raw reply related

* [PATCH 6/9] http: Turn on verbose Curl messages if GIT_CURL_VERBOSE set in environment
From: Mark Wooding @ 2006-02-01 11:44 UTC (permalink / raw)
  To: git
In-Reply-To: <20060201112822.5042.41256.stgit@metalzone.distorted.org.uk>

From: Mark Wooding <mdw@distorted.org.uk>

Signed-off-by: Mark Wooding <mdw@distorted.org.uk>
---

 http.c |    3 +++
 1 files changed, 3 insertions(+), 0 deletions(-)

diff --git a/http.c b/http.c
index 521323c..d0c92dc 100644
--- a/http.c
+++ b/http.c
@@ -192,6 +192,9 @@ static CURL* get_curl_handle(void)
 
 	curl_easy_setopt(result, CURLOPT_FOLLOWLOCATION, 1);
 
+	if (getenv("GIT_CURL_VERBOSE"))
+		curl_easy_setopt(result, CURLOPT_VERBOSE, 1);
+
 	return result;
 }
 

^ permalink raw reply related

* [PATCH 7/9] http-fetch: Tidy control flow in process_alternate_response
From: Mark Wooding @ 2006-02-01 11:44 UTC (permalink / raw)
  To: git
In-Reply-To: <20060201112822.5042.41256.stgit@metalzone.distorted.org.uk>

From: Mark Wooding <mdw@distorted.org.uk>

It's a bit convoluted.  Tidy it up.

Signed-off-by: Mark Wooding <mdw@distorted.org.uk>
---

 http-fetch.c |    7 ++-----
 1 files changed, 2 insertions(+), 5 deletions(-)

diff --git a/http-fetch.c b/http-fetch.c
index f1aac14..4aa5a11 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -468,12 +468,9 @@ static void process_alternates_response(
 			if (!slot->in_use)
 				active_requests++;
 			slot->in_use |= SLOTUSE_ACTIVE;
-			if (start_active_slot(slot)) {
-				return;
-			} else {
+			if (!start_active_slot(slot))
 				got_alternates = -1;
-				return;
-			}
+			return;
 		}
 	} else if (slot->curl_result != CURLE_OK) {
 		if (slot->http_code != 404 &&

^ permalink raw reply related

* [PATCH 8/9] http: Paranoid sanity checking for active slots.
From: Mark Wooding @ 2006-02-01 11:44 UTC (permalink / raw)
  To: git
In-Reply-To: <20060201112822.5042.41256.stgit@metalzone.distorted.org.uk>

From: Mark Wooding <mdw@distorted.org.uk>

Probably not wanted in the mainline, but very useful when debugging.

Signed-off-by: Mark Wooding <mdw@distorted.org.uk>
---

 http-fetch.c |    2 ++
 http.c       |   28 ++++++++++++++++++++++++++++
 http.h       |    1 +
 3 files changed, 31 insertions(+), 0 deletions(-)

diff --git a/http-fetch.c b/http-fetch.c
index 4aa5a11..fa3eb4a 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -465,9 +465,11 @@ static void process_alternates_response(
 				base);
 			curl_easy_setopt(slot->curl, CURLOPT_URL,
 					 alt_req->url);
+			sanity_check_active_slots();
 			if (!slot->in_use)
 				active_requests++;
 			slot->in_use |= SLOTUSE_ACTIVE;
+			sanity_check_active_slots();
 			if (!start_active_slot(slot))
 				got_alternates = -1;
 			return;
diff --git a/http.c b/http.c
index d0c92dc..8087ca0 100644
--- a/http.c
+++ b/http.c
@@ -1,3 +1,4 @@
+#include <assert.h>
 #include "http.h"
 
 int data_received;
@@ -348,19 +349,36 @@ struct active_request_slot *get_active_s
 	return slot;
 }
 
+void sanity_check_active_slots(void)
+{
+	struct active_request_slot *slot;
+	int n = 0;
+
+	for (slot = active_queue_head; slot; slot = slot->next) {
+		assert(!(slot->in_use & SLOTUSE_REF) == !slot->nrefs);
+		if (slot->in_use)
+			n++;
+	}
+	assert(n == active_requests);
+}
+
 int start_active_slot(struct active_request_slot *slot)
 {
+	sanity_check_active_slots();
 #ifdef USE_CURL_MULTI
 	CURLMcode curlm_result = curl_multi_add_handle(curlm, slot->curl);
 
 	if (curlm_result != CURLM_OK &&
 	    curlm_result != CURLM_CALL_MULTI_PERFORM) {
+		assert(slot->in_use & SLOTUSE_ACTIVE);
 		slot->in_use &= ~SLOTUSE_ACTIVE;
 		if (!slot->in_use)
 			active_requests--;
+		assert(active_requests >= 0);
 		return 0;
 	}
 #endif
+	sanity_check_active_slots();
 	return 1;
 }
 
@@ -382,20 +400,26 @@ void step_active_slots(void)
 
 static void watch_active_slot(struct active_request_slot *slot)
 {
+	sanity_check_active_slots();
 	if (!slot->in_use)
 		active_requests++;
 	slot->in_use |= SLOTUSE_REF;
 	slot->nrefs++;
+	sanity_check_active_slots();
 }
 
 static void unwatch_active_slot(struct active_request_slot *slot)
 {
+	sanity_check_active_slots();
+	assert(slot->nrefs);
 	slot->nrefs--;
 	if (!slot->nrefs) {
 		slot->in_use &= ~SLOTUSE_REF;
 		if (!slot->in_use)
 			active_requests--;
+		assert(active_requests >= 0);
 	}
+	sanity_check_active_slots();
 }
 
 void run_active_slot(struct active_request_slot *slot)
@@ -445,9 +469,13 @@ void run_active_slot(struct active_reque
 
 static void closedown_active_slot(struct active_request_slot *slot)
 {
+	sanity_check_active_slots();
+	assert(slot->in_use & SLOTUSE_ACTIVE);
         slot->in_use &= ~SLOTUSE_ACTIVE;
 	if (!slot->in_use)
 		active_requests--;
+	assert(active_requests >= 0);
+	sanity_check_active_slots();
 }
 
 void release_active_slot(struct active_request_slot *slot)
diff --git a/http.h b/http.h
index d028a5d..a2e48e8 100644
--- a/http.h
+++ b/http.h
@@ -59,6 +59,7 @@ extern int start_active_slot(struct acti
 extern void run_active_slot(struct active_request_slot *slot);
 extern void finish_all_active_slots(void);
 extern void release_active_slot(struct active_request_slot *slot);
+extern void sanity_check_active_slots(void);
 
 #ifdef USE_CURL_MULTI
 extern void fill_active_slots(void);

^ permalink raw reply related

* [PATCH 9/9] http-fetch: Paranoid sanity checking for the object queue.
From: Mark Wooding @ 2006-02-01 11:44 UTC (permalink / raw)
  To: git
In-Reply-To: <20060201112822.5042.41256.stgit@metalzone.distorted.org.uk>

From: Mark Wooding <mdw@distorted.org.uk>

Probably not wanted in the mainline, but useful for debugging.

Signed-off-by: Mark Wooding <mdw@distorted.org.uk>
---

 http-fetch.c |   70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 70 insertions(+), 0 deletions(-)

diff --git a/http-fetch.c b/http-fetch.c
index fa3eb4a..80eaa2f 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -3,6 +3,7 @@
 #include "pack.h"
 #include "fetch.h"
 #include "http.h"
+#include <assert.h>
 
 #define PREV_BUF_SIZE 4096
 #define RANGE_HEADER_SIZE 30
@@ -28,6 +29,13 @@ enum object_request_state {
 	COMPLETE,
 };
 
+typedef struct stash {
+	struct stash *next;
+	unsigned char sha1[20];
+	int activep;
+	int tickp;
+} stash;
+
 struct object_request
 {
 	unsigned char sha1[20];
@@ -47,6 +55,7 @@ struct object_request
 	int rename;
 	struct active_request_slot *slot;
 	struct object_request *next;
+	stash *tick;
 };
 
 struct alternates_request {
@@ -57,8 +66,60 @@ struct alternates_request {
 	int http_specific;
 };
 
+static stash *seen = 0;
+
+static stash *findstash(unsigned char *sha1)
+{
+	stash *s;
+	for (s = seen; s; s = s->next) {
+		if (memcmp(sha1, s->sha1, 20) == 0)
+			return s;
+	}
+	return NULL;
+}
+
+static stash *enstash(unsigned char *sha1)
+{
+	stash *s;
+	if ((s = findstash(sha1)) != NULL)
+		assert(!s->activep);
+	else {
+		s = xmalloc(sizeof(*s));
+		memcpy(s->sha1, sha1, 20);
+		s->next = seen;
+		seen = s;
+	}
+	s->activep = 1;
+	return s;
+}
+
+static void unstash(unsigned char *sha1)
+{
+	stash *s = findstash(sha1);
+	assert(s && s->activep);
+	s->activep = 0;
+}
+
 static struct object_request *object_queue_head = NULL;
 
+static void sanity_check_stash(void)
+{
+	stash *s;
+	struct object_request *obj_req;
+
+	for (s = seen; s; s = s->next)
+		s->tickp = 0;
+	for (obj_req = object_queue_head; obj_req; obj_req = obj_req->next) {
+		assert(obj_req->tick);
+		s = obj_req->tick;
+		assert(memcmp(s->sha1, obj_req->sha1, 20) == 0);
+		assert(s->activep);
+		s->tickp = 1;
+	}
+	for (s = seen; s; s = s->next)
+		assert(!s->tickp == !s->activep);
+}
+
 static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
 			       void *data)
 {
@@ -287,6 +348,7 @@ static void release_object_request(struc
 {
 	struct object_request *entry = object_queue_head;
 
+	sanity_check_stash();
 	if (obj_req->local != -1)
 		error("fd leakage in release: %d", obj_req->local);
 	if (obj_req == object_queue_head) {
@@ -298,6 +360,8 @@ static void release_object_request(struc
 			entry->next = entry->next->next;
 	}
 
+	unstash(obj_req->sha1);
+	sanity_check_stash();
 	free(obj_req->url);
 	free(obj_req);
 }
@@ -309,6 +373,7 @@ void fill_active_slots(void)
 	struct active_request_slot *slot = active_queue_head;
 	int num_transfers;
 
+	sanity_check_stash();
 	while (active_requests < max_requests && obj_req != NULL) {
 		if (obj_req->state == WAITING) {
 			if (has_sha1_file(obj_req->sha1))
@@ -327,6 +392,7 @@ void fill_active_slots(void)
 		}
 		slot = slot->next;
 	}				
+	sanity_check_stash();
 }
 #endif
 
@@ -347,6 +413,7 @@ void prefetch(unsigned char *sha1)
 		 "%s.temp", filename);
 	newreq->next = NULL;
 
+	sanity_check_stash();
 	if (object_queue_head == NULL) {
 		object_queue_head = newreq;
 	} else {
@@ -356,6 +423,8 @@ void prefetch(unsigned char *sha1)
 		}
 		tail->next = newreq;
 	}
+	newreq->tick = enstash(sha1);
+	sanity_check_stash();
 
 #ifdef USE_CURL_MULTI
 	fill_active_slots();
@@ -789,6 +858,7 @@ static int fetch_object(struct alt_base 
 	int ret = 0;
 	struct object_request *obj_req = object_queue_head;
 
+	sanity_check_stash();
 	while (obj_req != NULL && memcmp(obj_req->sha1, sha1, 20))
 		obj_req = obj_req->next;
 	if (obj_req == NULL)

^ permalink raw reply related

* Re: [PATCH] Shallow clone: low level machinery.
From: Johannes Schindelin @ 2006-02-01 14:33 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <7vzmlcz28x.fsf@assigned-by-dhcp.cox.net>

Hi,

On Tue, 31 Jan 2006, Junio C Hamano wrote:

> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> > Worse, you cannot pull from older servers into shallow repos.
> 
> "have X" means different thing if you do not have matching
> grafts information, so I suspect that is fundamentally
> unsolvable.

If the shallow-capable client could realize that the server is not 
shallow-capable *and* the local repo is shallow, and refuse to operate 
(unless called with "-f", in which case the result may or may not be a 
broken repo, which has to be fixed up manually by copying 
over ORIG_HEAD to HEAD).

Of course, the client has to know that the local repo is shallow, which it 
must not determine by looking at the grafts file.
 
> I am not sure you can convince "git-rev-list ^A" to mean "not at
> A but things before that is still interesting", especially when
> you give many other heads to start traversing from, but if you
> can, then you can do things at rev-list command line parameter
> level without doing the "exchange and use the same grafts"
> trickery.  That _might_ be easier to implement but I do not see
> an obvious correctness guarantee in the approach.

If you introduce a different "have X" -- like "have-no-parent X" -- and 
teach git-rev-list that "~A" means "traverse the tree of A, but not A's 
parents", you'd basically have everything you need, right?

> Implementation bugs aside, it is obvious the things _would_ work 
> correctly with "exchange and use the same grafts" approach.

Yes, I agree. But again, the local repo has to know which grafts were 
introduced by making the repo shallow.

Ciao,
Dscho

^ permalink raw reply

* Re: [Census] So who uses git?
From: Johannes Schindelin @ 2006-02-01 14:43 UTC (permalink / raw)
  To: Joel Becker
  Cc: Linus Torvalds, Keith Packard, Carl Baldwin, Junio C Hamano,
	Martin Langhoff, Git Mailing List
In-Reply-To: <20060131225514.GC2812@ca-server1.us.oracle.com>

Hi,

On Tue, 31 Jan 2006, Joel Becker wrote:

> On Tue, Jan 31, 2006 at 11:21:52AM -0800, Linus Torvalds wrote:
> > Now, I do agree. I don't actually like hiding the index too much. 
> > Understanding the index is _invaluable_ whenever you're doing a merge with 
> > conflicts, and understanding what tools are available to you to resolve 
> > those conflicts.
> 
> 	This is precisely the experience I've had explaining GIT to
> folks moving to it.  The simplest workflow (clone; hack one file, commit
> one file) is so similar to CVS/Subversion/Anything that it's immediately
> understood.  But when pull, push, merge, and any non-linear history are
> discussed, I have to describe the index and the commit/tree layout.
> Once I do, they get it.
> 
> > So I'm actually of the "revel in the index" camp (as could probably be 
> > guessed by the original tutorial).
> 
> 	I'm going to second this, from a real-world "explain it to
> others" standpoint.

How about talking about the index a bit at the end of tutorial.txt like 
this:

-- snip --
For a number of (mostly technical) reasons, "git diff" does not show the 
changes of the current working directory with respect to the latest 
commit, but rather to an intermediate stage: the "index".

Think of the index as a staging area just before committing: the commit 
object (and the tree and blob objects referenced from it) are assembled 
there.

Also, when you checkout, the index is used to disassemble the commit 
object just before writing the corresponding files and directories.
-- snap --

May this be worth the work?

Ciao,
Dscho

^ 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