* 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
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox