Git development
 help / color / mirror / Atom feed
* Re: The merge from hell...
From: Marco Costalba @ 2006-02-02  7:25 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Junio C Hamano, Git Mailing List, Paul Mackerras, Marco Costalba,
	Aneesh Kumar
In-Reply-To: <Pine.LNX.4.64.0602012212200.21884@g5.osdl.org>

On 2/2/06, Linus Torvalds <torvalds@osdl.org> wrote:
>

> Btw, I think you're better off parsing the "git-diff-tree --cc" output
> than doing it yourself like gitk does, now that core git has support for
> things like that.
>

Currently the public git repo version of qgit uses "git-diff-tree -c"
for merges, It's not a problem to change qgit to use --cc option
instead. But I would like to use just one kind of option to filter
merges files.

So I would like to hear some feedback on using --cc vs -c.

Marco

^ permalink raw reply

* Re: gitview 0.3
From: Aneesh Kumar @ 2006-02-02  7:15 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <7vzmlauw35.fsf@assigned-by-dhcp.cox.net>

On 2/2/06, Junio C Hamano <junkio@cox.net> wrote:
> Nhe choice of colors is intuitive).
>
> It might match more people's expectation if --with-diff were the
> default.
>

Done. I made it to take option --without-diff for guys like me


> This patch might make merges easier to read.
>
> -- >8 --
> [PATCH] Use "diff-tree -c" to display merges a bit more readably.
>
> --- a/gitview   2006-02-01 21:16:43.000000000 -0800
> +++ b/gitview   2006-02-01 21:46:24.000000000 -0800
> @@ -305,7 +305,7 @@
>                 return message
>
>         def diff_tree(self):
> -               fp = os.popen("git diff-tree --pretty -m -v -p " + " " + self.commit_sha1)
> +               fp = os.popen("git diff-tree --pretty -c -v -p " + " " + self.commit_sha1)
>                 diff = fp.read()
>                 fp.close()
>
>
>
>


I applied this patch . But the problem now is merge commits doesn't
show the commit messages. Adding -m also didn't help. Any suggestion

-aneesh

^ permalink raw reply

* Re: The merge from hell...
From: Junio C Hamano @ 2006-02-02  7:05 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Git Mailing List, Paul Mackerras, Marco Costalba, Aneesh Kumar
In-Reply-To: <Pine.LNX.4.64.0602012212200.21884@g5.osdl.org>

Linus Torvalds <torvalds@osdl.org> writes:

>  I've been holding off on having octopus merges in the kernel tree, but I 
> just merged an ACPI update that had used one of them, and I didn't really 
> see any real reason not to take it.
>
> That octopus commit has got _twelve_ parents.

Len must have been smoking something really good.  Even I would
not attempt to do such an octopus and expect to keep my sanity,
but why not? ;-)

> It's commit 9fdb62af92c741addbea15545f214a6e89460865, and passing it to 
> git-diff-tree with the "--cc" option seems to do the largely the right 
> thing (although arguably, since one of the parents always matches the end 
> result in all the files, it shouldn't have shown anything at all, so I 
> think it could do with some tweaking).

Hmph.  Do you mean a hunk like this?

diff --cc kernel/sys.c
@@@@@@@@@@@@@ +33,7 @@@@@@@@@@@@@

            #include <linux/compat.h>
            #include <linux/syscalls.h>
   + +++    #include <linux/kprobes.h>

            #include <asm/uaccess.h>
            #include <asm/io.h>

Currently I cull hunks that have changes from only one parent,
or when the changes are the same from all but one parent.  This
hunk does not match either criteria.

Instead I could cull all hunks that have either "all whitespace"
or "plus and minus in the same set of columns" hunks.  If I did
so, this would become uninteresting:

diff --cc arch/ia64/pci/pci.c
@@@@@@@@@@@@@ +709,7 @@@@@@@@@@@@@
             */
            int ia64_pci_legacy_write(struct pci_dev *bus, u16...
            {
  ------ -  	int ret = 0;
  ++++++ +  	int ret = size;
            
            	switch (size) {
            	case 1:

But this is still interesting:

@@@@@@@@@@@@@ +308,35 @@@@@@@@@@@@@
            			goto end;
            		}
            	}
  --        	cx->usage++;
  --        
            
     +++    #ifdef CONFIG_HOTPLUG_CPU
     +++    	/*


> git-diff-tree takes almost three seconds to get its result, though.

One trivial thing I should be able to do to speed things up is
to reuse previous diff with other parents.  For example, that
commit does this to kernel/sys.c from its 12 parents.

:100644 100644 d09cac2... 0929c69... M  kernel/sys.c
:100644 100644 eecf845... 0929c69... M  kernel/sys.c
:100644 100644 c3b1874... 0929c69... M  kernel/sys.c
:100644 100644 bce933e... 0929c69... M  kernel/sys.c
:100644 100644 eecf845... 0929c69... M  kernel/sys.c
:100644 100644 bce933e... 0929c69... M  kernel/sys.c
:100644 100644 bce933e... 0929c69... M  kernel/sys.c
:100644 100644 bce933e... 0929c69... M  kernel/sys.c
:100644 100644 eecf845... 0929c69... M  kernel/sys.c
:100644 100644 eecf845... 0929c69... M  kernel/sys.c
:100644 100644 eecf845... 0929c69... M  kernel/sys.c
:100644 100644 eecf845... 0929c69... M  kernel/sys.c

Running "sort -u" on these would leave only 4 lines.

I did not expect anybody to be _that_ sick (eh, pardon my
language) to do a 12-way octpus, so I did not consider this
optimization possibility, but I should be doing only 4 diffs to
format -c for this commit.  Currently I do 12.

> So think of it as a correctness/scalability test.

Heh.  I like this one as a practice.  Thanks!

^ permalink raw reply

* The merge from hell...
From: Linus Torvalds @ 2006-02-02  6:28 UTC (permalink / raw)
  To: Junio C Hamano, Git Mailing List, Paul Mackerras, Marco Costalba,
	Aneesh Kumar


Ok,
 I've been holding off on having octopus merges in the kernel tree, but I 
just merged an ACPI update that had used one of them, and I didn't really 
see any real reason not to take it.

That octopus commit has got _twelve_ parents.

It's commit 9fdb62af92c741addbea15545f214a6e89460865, and passing it to 
git-diff-tree with the "--cc" option seems to do the largely the right 
thing (although arguably, since one of the parents always matches the end 
result in all the files, it shouldn't have shown anything at all, so I 
think it could do with some tweaking).

git-diff-tree takes almost three seconds to get its result, though.

However, if git-diff-tree is a bit slow, then gitk gets _really_ upset 
when you click on it. Apparently the colorized multi-way diff just breaks 
down into some O(2**n) hell.

I'm sure it's making progress, it's just very slow, and gitk is 
essentially unusable on that commit (gitk also gets visually confused by 
the fact that it forces a wider set of history than gitk expects, since it 
can't prune it down with the arrow notation).

[ Ahh. gitk just came back. After a _loong_ time thinking, and having run
  out of different colors, but looking otherwise fairly nice ;]

So think of it as a correctness/scalability test.

I haven't tried qgit or gitview on it, but am cc'ing Marco and Aneesh in 
case they want to hone their octopus skills on it.

Btw, I think you're better off parsing the "git-diff-tree --cc" output 
than doing it yourself like gitk does, now that core git has support for 
things like that.

			Linus

^ permalink raw reply

* Re: gitview 0.3
From: Junio C Hamano @ 2006-02-02  6:13 UTC (permalink / raw)
  To: Aneesh Kumar; +Cc: git
In-Reply-To: <cc723f590602012033w41b49b2ao4423707702086739@mail.gmail.com>

Nice work for 0.low-number version.

On my notebook, I felt really miserable to see that the middle
band taking so much space to show rev committer timestamp and
parents.  The bottom window starts with the same information
repeated (worse, with unreadable timestamp format) and I had to
scroll all the way down to view the rest of the commit message.

If you absolutely want the middle band, maybe instead of showing
object names in "Parents" "Children" part, you can show the
one-line commit logs from them there, and pop-up the object
names along with the rest of commit log message as mouse hovers
over.

I missed "this commit is branch head of X" and "this commit is
tagged with T" markers gitk has.

Needs a bit more compact layout to be useful for me.  In short,
gitk's display does everything I want it to show in a compact
enough way and I think there is no reason not to imitate it.

Do colors of nodes have any significance?  I couldn't tell from
the UI (and I tried to figure it out without reading code --
otherwise I cannot tell if the choice of colors is intuitive).

It might match more people's expectation if --with-diff were the
default.

This patch might make merges easier to read.

-- >8 --
[PATCH] Use "diff-tree -c" to display merges a bit more readably.

--- a/gitview	2006-02-01 21:16:43.000000000 -0800
+++ b/gitview	2006-02-01 21:46:24.000000000 -0800
@@ -305,7 +305,7 @@
 		return message
 
 	def diff_tree(self):
-		fp = os.popen("git diff-tree --pretty -m -v -p " + " " + self.commit_sha1)
+		fp = os.popen("git diff-tree --pretty -c -v -p " + " " + self.commit_sha1)
 		diff = fp.read()
 		fp.close()
 

^ permalink raw reply

* Re: Question around git-shell usage in Everyday Git
From: Junio C Hamano @ 2006-02-02  5:44 UTC (permalink / raw)
  To: Alan Chandler; +Cc: git
In-Reply-To: <200602020517.05827.alan@chandlerfamily.org.uk>

Alan Chandler <alan@chandlerfamily.org.uk> writes:

> Then each developer would access the repository via
>
> URL:home.chandlerfamily.org.uk:projectx.git
>
> but would be only allowed access if they were on a white list within each 
> project.

This is the part I highly doubt is doable -- without having
separate unix level UID your whitelist mechanism would not work.
And I do not think you would get separate unix level UID without
having separate $HOME/.ssh directory, even if you have prepared
separate unix level UID in your /etc/passwd file.

Come to think of it, it is worse than that.  IIRC, sshd has
rather strict check to make sure that only one unix user can
write into $HOME/.ssh/ directory (obviously the owner of that
$HOME directory, which means the user who has the directory as
her home directory in /etc/passwd).  If more than one unix level
user shares a home directory, I do not think you can satisfy
that checking.

Maybe it does not matter, since they will be pushing the commits
with their name set to committer/author fields and if you trust
them, but then there is no point assigning one UID per user.

One UID per project is probably doable but I do not think that
is a useful arrangement either.

> So the third approach I am now contemplating is to actually create separate 
> users for each project

You still cannot distinguish your users in a project with each
other, which may or may not matter to you.  Also this is
inconvenient for your developer who works on more than one of
your projects -- I think he needs to use one project identity
for each.

So in short, I am mildly negative about this.  If I were doing
this kind of thing I'd have one unix UID and one directory per
one physical user.  What is the real reason (other than "I just
do not want to" feeling) you want to have smaller number of home
directories than you have users?

^ permalink raw reply

* Re: Question around git-shell usage in Everyday Git
From: Alan Chandler @ 2006-02-02  5:17 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Junio C Hamano, git
In-Reply-To: <Pine.LNX.4.64.0602011530530.21884@g5.osdl.org>

On Wednesday 01 February 2006 23:37, Linus Torvalds wrote:
> On Wed, 1 Feb 2006, Junio C Hamano wrote:
> > Do you mean to reuse single directory /home/gitu/ for user A, B, C,
> > and hang repositories /home/gitu/{X,Y,Z} for projects?  I'd
> > imagine things could be arranged that way.  User A and B but not
> > C may be in "projectX" group and /home/gitu/X is writable only
> > by projectX group members and such...
>
> That would work from a _git_ angle, but I don't think that was what Alan
> was talking about, and it would failr horribly from a "ssh" perspective
> (because "ssh" will want $HOME/.ssh/authorized_keys etc).
>
> So I _think_ what Alan was describing was just a
>
> 	/home/user-A/project.git
> 		     another-project.git
> 		     ya-project.git
> 	      user-B/myproject.git
> 	      user-C/..
>

Actually Junio was right in layout - but I am not sure he understands what I 
want to achieve.

What I have in mind is that I am a personal developer at home with my own 
server.  I have written various code in a number of projects whose public 
repositories are all located under /var/lib/git. 
(ie /var/lib/git/projectA.git, /var/lib/git/projectB.git) and they are all 
shareable.

git-daemon is run with base-path as /var/lib/git and have defined user "git" 
to have a home directory of /var/lib/git and git-shell as his shell, so 
anyone can clone from them

So, if my projects take off, and I have some outside developers helping me, I 
do not want to give them individual home directories on my server instead my 
plan_had_ been to collect their public keys at put them 
in /var/lib/git/.ssh/authorized_keys, and then tell them to push to the 
repository via

URL:git@home.chandlerfamily.org.uk:projectA.git

etc.

The downside of this approach is that all developers get access to all 
repositories - maybe I need better control.


Then I saw the howto on repository control with the update hook and was 
thinking that I could do it as follows

in /etc/password create an entry for each developer with a home directory 
of /var/lib/git and git shell. (so I DO NOT create a large number of /home 
directories at all) and make them members of group git.

Then each developer would access the repository via

URL:home.chandlerfamily.org.uk:projectx.git

but would be only allowed access if they were on a white list within each 
project.

Thats when I asked the question that started this thread.  

Since then I have realised that the downside of this is that its pretty easy 
to fake being someone else and getting access by just giving someone elses 
name.

So the third approach I am now contemplating is to actually create separate 
users for each project

so user projectA had a home directory of /var/lib/git/projectA.git with a 
subdirectory of .ssh for the authorized keys file.

Super users (e.g. ME) would have their keys 
in /var/lib/git/.ssh/authorized_keys

Individual project developers would have their keys in 

/var/lib/git/projectA.git/.ssh/authorized_keys etc

and would access the repository with

URL:ProjectA@home.chandlerfamily.org.uk:.

 
-- 
Alan Chandler
http://www.chandlerfamily.org.uk
Open Source. It's the difference between trust and antitrust.

^ permalink raw reply

* gitview 0.3
From: Aneesh Kumar @ 2006-02-02  4:33 UTC (permalink / raw)
  To: git, junkio

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

Attaching below the latest code

Changes from 0.1
* use diff-tree -m to generate the diff with --with-diff option.
* Some code cleanup
* Added --with-diff option
* Fixed typo (Dave Jones)

-aneesh

[-- Attachment #2: gitview --]
[-- Type: application/octet-stream, Size: 24759 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 """

	children_sha1 = {}

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


	def parse_commit(self, commit_lines):

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

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

		# IF we don't have parent
		if (len(self.parent_sha1) == 0):
			self.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 self.message == "":
					self.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)	
				self.author = list_to_string(patch_author[1:-2], 0)
				self.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)	
				self.committer = list_to_string(patch_committer[1:-2], 0)
				self.commit_date = time.strftime("%Y-%m-%d %H:%M:%S",
						time.gmtime(float(patch_committer[-2])))
				continue

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

		return message

	def diff_tree(self):
		fp = os.popen("git diff-tree --pretty -m -v -p " + " " + self.commit_sha1)
		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 = Commit.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 = {}

		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 = 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 _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

* Re:
From: H. Peter Anvin @ 2006-02-02  4:18 UTC (permalink / raw)
  To: Jason Riedy; +Cc: git
In-Reply-To: <24768.1138840762@lotus.CS.Berkeley.EDU>

Jason Riedy wrote:
> I guess our home directories recently were changed from symlinks
> to autmounts.  Solaris 8's mkdir(2) returns ENOSYS when applied
> to these, breaking safe_create_leading_directories.  I don't
> know if ENOSYS is available everywhere, or if this odd behavior
> is appropriate everywhere.
> 
> This works for me, but should I wrap mkdir for bizarre behavior
> by adding a compat/gitmkdir.c?

Wow, Solaris really can be braindamaged sometimes...

	-hpa

^ permalink raw reply

* Re: [RFC & PATCH] Solaris 8: ENOSYS when mkdir applied to automount.
From: Jason Riedy @ 2006-02-02  4:14 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <7vwtge37w5.fsf@assigned-by-dhcp.cox.net>

And Junio C Hamano writes:
 - There was a similar patch for working around Cygwin that threw
 - different errno in an earlier thread:

Thanks.  The thread mentioned in that one seems to have a few
"right" solutions.  A compat/gitmkdirat.c likely is the best,
modelled after the gnulib one.  I'll look at it when I can in 
the next week or so, so you can ignore my patch.

 - Somehow I started to trust your ability to code portably a lot
 - better than I trust myself, [...]

eep.  ;)  I only have access to three major variations at the
moment (Linux, semi-old Solaris, recent AIX), so I'm not an
authority...  Solaris 8 is from before the general Linux/glibc 
compatability movement, so it's pretty useful for testing.

Jason

^ permalink raw reply

* Re: [PATCH 0/9] http-fetch fixes
From: Junio C Hamano @ 2006-02-02  3:02 UTC (permalink / raw)
  To: Mark Wooding; +Cc: git, Uwe Zeisberger, Nick Hengeveld
In-Reply-To: <slrndu1m14.2i8.mdw@metalzone.distorted.org.uk>

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

> Uwe Zeisberger <zeisberg@informatik.uni-freiburg.de> wrote:
>
>> With these patches applied, I get now a Segfault, while cloning
>> u-boot.
> ...
> Maybe the right thing to do is to go with Nick's separate-status-
> structures patch as an actual mainline fix, with my patches for
> http-fetch.c's other bugs as a belt-and-braces.

Thanks.

I briefly looked at the series; the following may be ready to be
applied:

[PATCH 2/9] http-fetch: Fix object list corruption in fill_activ...
[PATCH 5/9] http-fetch: Fix message reporting rename of object file
[PATCH 6/9] http: Turn on verbose Curl messages if GIT_CURL_VERB...
[PATCH 7/9] http-fetch: Tidy control flow in process_alternate_r...

For now however I'll wait and see for a while, in case you can
reproduce the breakage Uwe found and add fixes to your set.

^ permalink raw reply

* Re: Two ideas for improving git's user interface
From: Junio C Hamano @ 2006-02-02  2:25 UTC (permalink / raw)
  To: Carl Worth; +Cc: git
In-Reply-To: <87irrya7bx.wl%cworth@cworth.org>

Carl Worth <cworth@cworth.org> writes:

> If not, we should be able to simplify things since a lot of the
> UI complexity being discussed (-a vs. no -a, path names vs. no path
> names), hinges on the handling of skewed files.

I am in agreement with you that "skewed files" might lead to
confusion, but I do not see how that relates to "-a vs no -a" nor
"path names vs no path names" issues.

Let's say we try to detect and forbid committing skewed files.  How
would we do that?  For the sake of clarity, let's say we fixed the
commit command the way I said in the message you are responding.

Now:

1. "git commit" is the traditional one; it commits the current index.
   We enumerate paths that 'git-diff-index --cached --name-only HEAD'
   tells are different (they are the paths to be committed -- what
   about merges?  Maybe take union from all parents?).  Then we see if
   the paths from "git-diff-files --name-only" (locally modified
   files) overlap with them.  Overlapping ones will be skewed if we
   make a commit.

2. "git commit --also fileA..." updates fileA... on top of the current
   index and commits that.  After doing "git update-index fileA...",
   the story is the same as the previous case.

3. "git commit fileA..." initializes a temporary index from the
   current HEAD, updates fileA... and commits that.  We would need a
   check to make sure index matches HEAD at specified paths, but after
   that check passes, there is no skewed files being committed and
   there is nothing more to check.

4. "git commit -a" by definition would not have skewed files and there
   is nothing to check.

So what you say sounds doable.  But I wonder if that really helps
much.

Let's say we want to give an interface to a class of users who do
_not_ want to worry about the presense of the index file.  That means
they will _never_ run "git update-index" themselves, although "git
commit", "git add", and "git merge" may run update-index for them
internally.  Essentially, you tell them to always use "git commit -a"
or "git commit fileA...", and do not teach them "git commit", "git
commit --also fileA...".  IOW they will be doing only 3 or 4.  In this
case, we do not need any of the "skewed files" check.

The extra checks in 1 and 2 would prevent index-unaware users from
making obvious mistakes, but if they do not understand index then they
would still be surprised anyway.  For example, "git commit" commits
the files they previously run "git add" on, but leaves other modified
files in the working tree uncommitted.  This is different from either
3 or 4 that they have learned so far.  If they did "git commit fileA",
the file earlier they run "git add" is not committed.  If they did
"git commit -a", files other than the added files are also committed.
So in that sense the above checks are doable but I do not think it
helps that much to alleviate the confusion.

These extra checks in 1 and 2 may protect index-aware users from
making mistakes, to a certain degree.  I am not convinced enough
myself to pay the cost of extra checks, though, because my workflow is
to do the final review exactly like what you said below.

> My workflow has been to always perform a final review of such a diff
> while composing the commit message. I'd like to be able to do that
> with git.

That matches my workflow.  I do either one of these (I never use "git
commit paths..."):

	$ work work work
        $ I may do update-index [--add|--remove] here
        $ git diff --cached
        $ git commit

	$ work work work
        $ I may do update-index [--add|--remove] here
        $ git diff HEAD
        $ git commit -a

In either cases "skewed files" do not matter.  This can be summarized
in a short paragraph:

	If you are going to commit with "git commit" (no parameters),
	check the final result with "git diff --cached".  If you are
	going to commit with "git commit -a", check with "git diff
	HEAD".

I said why I do not do "git commit paths..." myself, but I think this
"skewed files" discussion adds another thing to be careful about if
you use it.  If you do this (with the current tool, you drop --also):

	$ work on file A
        $ git diff A
        ... that looks fine so far ...
        $ git update-index A
        $ work more on file A
        $ git diff A
        ... incrementally that looks fine ...
        $ git commit --also A

you would end up commiting something you have not done the "final
review".  You need to have the final check before such a commit:

	$ work on file A
        $ git diff A
        ... that looks fine so far ...
        $ git update-index A
        $ work more on file A
        $ git diff A
        ... incrementally that looks fine ...
 +++++  $ git diff HEAD
        $ git commit --also A

This includes all changes that are not in the index and are not going
to be included in the commit (i.e. changes to files other than A).
For that you may need to do something like:

	git-diff-index --cached HEAD ;# already in index but do not look at A
        git-diff-index HEAD -- A ;# and path A is taken from working tree

which is a bit cumbersome.

Without --also (the new semantics), the check would be
straightforward:

	$ work on file A
        $ git diff A
        ... that looks fine so far ...
        $ git update-index A
        $ work more on file A
        $ git diff A
        ... incrementally that looks fine ...
 +++++  $ git diff HEAD -- A
	$ git commit A

^ permalink raw reply

* Re: Two ideas for improving git's user interface
From: Linus Torvalds @ 2006-02-02  1:44 UTC (permalink / raw)
  To: Carl Worth; +Cc: Junio C Hamano, Nicolas Pitre, git
In-Reply-To: <Pine.LNX.4.64.0602011656130.21884@g5.osdl.org>



On Wed, 1 Feb 2006, Linus Torvalds wrote:
> 
> And notice how I commit the _merge_ without actually committing my dirty 
> state in the tree - and whether the files involved in my standard dirty 
> changes ("Makefile") are part of the state that the merge changed or not 
> is _totally_ irrelevant.

If you get the feeling that merging is special, then to some degree, yes, 
you'd be right.

Merging (especially with conflicts) is the _one_ operation where you 
absolutely have to know about the index. If you don't know about how the 
index works, you can get the conflict resolution right kind of by 
accident, simply because the default workflow of

	.. edit conflict to look ok ..
	git commit file/with/conflict

actually happens to do exactly the right thing (very much on purpose, 
btw), but the fact is, to actually figure out more complicated conflicts 
and to _understand_ what happens, you absolutely need to be aware of the 
index. Not being aware of it just isn't an option for any serious git 
user.

(Btw, I think this is where cogito falls down. Cogito tries to hide the 
index file, but I don't think you really _can_ hide the index file and 
also do merges well at the same time. Anybody who has non-trivial merges 
should use raw git - not just because the "recursive" strategy just works 
better, but exactly because of the index file issue).

So when you work with a merge, the index file content really in a very 
real way _is_ the merge. Yes, the index file is also technically how git 
actually does all the merging complexity, but in this case, there also is 
no "diff" to the parent, and the number of changed files may be in the 
hundreds, yet "git diff" should be basically empty when you finally commit 
your merge.

I say "basically empty", because as I've explained, at least I personally 
have had dirty state in my tree at the time I commit a merge - on _top_ of 
(and independently of) the state that I actually commit.

So to recap:

 - you really do have to be aware of the index file at some point. Trying 
   to hide it entirely is a huge mistake.

 - real git power users _will_ use their awareness of the index file when 
   they commit. You will too, some day. Maybe it's only for merges, but I 
   wouldn't be surprised if somebody at some point wants to take advantage 
   of it even for "normal" working conditions (ie use "git-update-index" 
   to "freeze" a certain state for committing, and then editing the file 
   and _not_ committing those edits)

So making "-a" the default would be just a horrid horrid mistake. You can 
only hide the index so far - don't even try to hide it more.

			Linus

^ permalink raw reply

* Re: Two ideas for improving git's user interface
From: Linus Torvalds @ 2006-02-02  1:23 UTC (permalink / raw)
  To: Carl Worth; +Cc: Junio C Hamano, Nicolas Pitre, git
In-Reply-To: <87lkwupsbr.wl%cworth@cworth.org>



On Wed, 1 Feb 2006, Carl Worth wrote:
> 
> Here's a fundamental question I have, (and thanks to Keith Packard for
> helping me to phrase it):
> 
> 	Is it ever useful (reasonable, desirable) to commit file
> 	contents that differ from the contents of the working
> 	directory?

Yes. I do it all the time.

I tend to have a certain fairly constant set of changes in my working 
tree, namely every time a release is getting closer, I always tend to have 
the "Makefile" already updated for the new version (but not checked in: I 
do that just before I actually tag it, so that the tag will match the 
commit that actually changes the version).

I do that largely for historical reasons, namely that I've forgotten too 
many times to actually change the version number, and then I usually get a 
bug report within minutes of cutting the release with a snickering "hah, 
you forgot to change the version again".

So I do lots of commits with that Makefile being dirty, without ever 
actually committing the Makefile changes themselves. "git commit -a" as a 
default would be absolutely _horrible_ for me.

I occasionally have other things dirty too in my tree - just random 
hacking. But the Makefile is dirty about 50% of the time for me, so it's 
the common case.

And most of those commits are automated, either through pulls that are 
successful, or just my email patch-application scripts, and both of those 
cases actually check that the files that are _changed_ are never dirty in 
the working directory.

However, if the question was an even stricter "do you ever commit 
_changes_ to a particular file where the last HEAD, the index _and_ the 
working tree are all different", then the answer is actually "Yes" to that 
too.

What has happened is that I have had merges that have content conflicts 
that I fix up by hand, but exactly _because_ I fix them up by hand, I 
actually want to re-compile the kernel and test my fixups.

And in that case, I will actually re-apply my manual Makefile change, even 
if that file was part of the merge changes (in which case I had had to 
first un-apply the change in order to do the merge).

So what happens is that I recompile with my trivial changes in place 
_after_ I have fixed up any merge conflicts, reboot the thing to test, and 
then commit the result if everything looks ok.

And notice how I commit the _merge_ without actually committing my dirty 
state in the tree - and whether the files involved in my standard dirty 
changes ("Makefile") are part of the state that the merge changed or not 
is _totally_ irrelevant.

So I commit file contents that differ from my current working tree all the 
time.

ALMOST all of the time, the actual _changes_ that I commit do not actually 
touch the files that I have dirty, but as explained above, even that is 
not at all impossible.

The thing is, once you get used to the git "index" as a staging place, 
it's really really powerful. 

> Idea #2 (make it easy to preview diffs of what will be committed)
> -------
> Independent of the above, I'd like to propose another change to help
> prevent confusion and to help users learn git. There should be an
> obvious "diff" operation that presents exactly the result of what any
> "commit" operation will perform.

Actually, we do exactly that. Right now we expressly limit the "preview" 
to just the filenames, but we literally do run

	git-diff-index -M --cached --name-status --diff-filter=MDTCRA HEAD

as part of "git status", and the eventual end result is what we will 
populate the commit message file with for your editing pleasure.

And you can actually see that. 

So I would suggest that new git users never be told about the "-m" flag to 
"git commit", so that they always have to edit the commit message by hand, 
because that commit message will contain exactly this information.

Not the patch itself, though. Maybe we could make it show part of it, 
though, if somebody really wants to see it ;)

		Linus

^ permalink raw reply

* Re: Two ideas for improving git's user interface
From: Carl Worth @ 2006-02-02  1:16 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Nicolas Pitre, Linus Torvalds
In-Reply-To: <7v1wym4msq.fsf@assigned-by-dhcp.cox.net>

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

On Wed, 01 Feb 2006 16:38:45 -0800, Junio C Hamano wrote:
> 
> I do not think you have to make it sound *that* negative.

Sorry about that. I was just trying to emphasize the new-user
confusion, and perhaps I went overboard.

>          It is a useful timesaver to be able to leave
> unrelated changes around in the working tree.
> 
> > I don't think it is, (but please let me know if I've missed some
> > useful case).
> 
> I think I've already done this a couple of times today.

I'm sorry. I didn't succeed in phrasing the question the way I
wanted. Yes, it is useful to be able to leave unrelated changes around
in the working tree. So in that sense, it is clearly useful to be able
to commit something that is different (in a repository-wide sense)
than what is in the working tree.

The question I was trying to ask is, for a _single file_ is it ever
useful to commit contents that differ from the contents of the working
directory? Let's call this a "skewed file" in the index.

I haven't used git much yet, but I found two cases for when one might
end up committing a skewed file:

1) Modification of working directory after git-update-index or git-add.

   There has been discussion in this thread already that the user can
   get a confusing commit in this case.

2) git-read-tree -m	# without -u

   The git documentation already advertises that not using -u here
   leads to confusion. This one looks historical, and it's not obvious
   to me whether git-read-tree is used in practice without -u.

So, in both of those cases the skewed files seem to lead only to
confusion. Are there any non-confusing cases where it's useful to be
able to commit a skewed file?

If not, we should be able to simplify things since a lot of the
UI complexity being discussed (-a vs. no -a, path names vs. no path
names), hinges on the handling of skewed files.

> Your "git diff" is interesting, but I'd rather make them
> completely separate command from "git diff".  Perhaps "git
> ndiff" and "git ncommit", that assumes there is nothing but "git
> commit -a" kind of commits.

I'd be fine with some other name than "diff" if strictly necessary,
but I'm not suggesting something that makes any assumption about "git
commit -a" only. What I want is a simple way to take any "git commit"
command and be able to examine the diff that it will be committing.

My workflow has been to always perform a final review of such a diff
while composing the commit message. I'd like to be able to do that
with git.

And I think this tool would make a very good learning tool for users
trying to figure out the various commit operations, (particularly if
we end up with different semantics for merge vs. non-merge, -a vs. no
-a, path names vs. no path names, etc.).

-Carl

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

^ permalink raw reply

* Re: [PATCH] Shallow clone: low level machinery.
From: Junio C Hamano @ 2006-02-02  1:17 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git
In-Reply-To: <Pine.LNX.4.63.0602020113200.30910@wbgn013.biozentrum.uni-wuerzburg.de>

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

>> If you have such a modified rev-list, yes.  I was having doubts
>> about keeping an obvious correctness guarantee when doing such
>> "rev-list ~A".
>
> I think it would be trivial: just resolve ~A to the tree A points to:

<tired> Hmph.  I thought you meant "have-only A" to mean similar
to "have A" but additionally "do not assume I have things behind
A", and are going to extend rev-list to support ~A syntax to do
that.  I am a bit surprised to see your "rev-list ~A" is to
include A, not exclude A and not what are behind A.  Where is
the connection between this and "have-only A"?  </tired> ;-)

>> > Yes, I agree. But again, the local repo has to know which grafts were 
>> > introduced by making the repo shallow.
>> 
>> I am not sure I understand.  grafts are grafts are grafts.
>
> Exactly. And grafts are grafts are not necessarily cutoffs.
>
> Now, is it possible that a fetch does something unintended, when there are 
> grafts which are not cutoffs? I don't know yet, but I think so.

I think we are disagreeing, so "not Exactly".  I meant "grafts
are grafts, there is no cutoffs, they are also just grafts".  So
the answer to your question is "it does not matter".

^ permalink raw reply

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

Hi,

On Wed, 1 Feb 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).
> 
> "If ... refuse to operate" then?

Just skip the "If". I'll start to enclose all emails I write in 
<tired>..</tired> blocks.

> > Of course, the client has to know that the local repo is shallow, which it 
> > must not determine by looking at the grafts file.
> 
> Sorry, I fail to understand this requirement.  Why is it "it must not"?

See below.

> > 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?
> 
> If you have such a modified rev-list, yes.  I was having doubts
> about keeping an obvious correctness guarantee when doing such
> "rev-list ~A".

I think it would be trivial: just resolve ~A to the tree A points to:

-- snip --
[PATCH] rev-list: Support "~treeish"

Now, "git rev-list --objects ~some_rev" traverses just the tree of
some_rev.

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>

---

 rev-list.c |   23 +++++++++++++++++++++++
 1 files changed, 23 insertions(+), 0 deletions(-)

43267e65c9ad933ad1a49005c4b61c23adaec372
diff --git a/rev-list.c b/rev-list.c
index 8012762..a196110 100644
--- a/rev-list.c
+++ b/rev-list.c
@@ -720,6 +720,24 @@ static void handle_one_commit(struct com
 	commit_list_insert(com, lst);
 }
 
+static void handle_tree(const unsigned char *sha1)
+{
+	struct object *object;
+
+	object = parse_object(sha1);
+	if (!object)
+		die("bad object %s", sha1_to_hex(sha1));
+
+	if (object->type == tree_type)
+		add_pending_object(object, "");
+	else if (object->type == commit_type) {
+		struct commit *commit = (struct commit *)object;
+		if (parse_commit(commit) < 0)
+			die("unable to parse commit %s", sha1_to_hex(sha1));
+		add_pending_object(&(commit->tree->object), "");
+	}
+}
+
 /* for_each_ref() callback does not allow user data -- Yuck. */
 static struct commit_list **global_lst;
 
@@ -865,6 +883,11 @@ int main(int argc, const char **argv)
 			flags = UNINTERESTING;
 			arg++;
 			limited = 1;
+		} else if (*arg == '~') {
+			if (get_sha1(arg + 1, sha1) < 0)
+				die("cannot get '%s'", arg);
+			handle_tree(sha1);
+			continue;
 		}
 		if (get_sha1(arg, sha1) < 0) {
 			struct stat st;
-- 
1.1.4.g9bd9d-dirty
-- snap --

> > Yes, I agree. But again, the local repo has to know which grafts were 
> > introduced by making the repo shallow.
> 
> I am not sure I understand.  grafts are grafts are grafts.

Exactly. And grafts are grafts are not necessarily cutoffs.

Now, is it possible that a fetch does something unintended, when there are 
grafts which are not cutoffs? I don't know yet, but I think so.

Ciao,
Dscho

^ permalink raw reply related

* Re: [RFC & PATCH] Solaris 8: ENOSYS when mkdir applied to automount.
From: Junio C Hamano @ 2006-02-02  0:46 UTC (permalink / raw)
  To: Jason Riedy; +Cc: git
In-Reply-To: <24768.1138840762@lotus.CS.Berkeley.EDU>

Jason Riedy <ejr@EECS.Berkeley.EDU> writes:

> I guess our home directories recently were changed from symlinks
> to autmounts.  Solaris 8's mkdir(2) returns ENOSYS when applied
> to these, breaking safe_create_leading_directories.  I don't
> know if ENOSYS is available everywhere, or if this odd behavior
> is appropriate everywhere.
>
> This works for me, but should I wrap mkdir for bizarre behavior
> by adding a compat/gitmkdir.c?

There was a similar patch for working around Cygwin that threw
different errno in an earlier thread:

    From: Alex Riesen <raa.lkml@gmail.com>
    Subject: [PATCH] Set errno to EEXIST if mkdir returns EACCES or EPERM

Somehow I started to trust your ability to code portably a lot
better than I trust myself, so please first disregard the
suggestion I gave in that thread and give me your honest opinion
on what the right fix/workaround would be.

^ permalink raw reply

* [RFC & PATCH] Solaris 8: ENOSYS when mkdir applied to automount., 
From: Jason Riedy @ 2006-02-02  0:39 UTC (permalink / raw)
  To: git

I guess our home directories recently were changed from symlinks
to autmounts.  Solaris 8's mkdir(2) returns ENOSYS when applied
to these, breaking safe_create_leading_directories.  I don't
know if ENOSYS is available everywhere, or if this odd behavior
is appropriate everywhere.

This works for me, but should I wrap mkdir for bizarre behavior
by adding a compat/gitmkdir.c?

Signed-off-by: Jason Riedy <ejr@cs.berkeley.edu>

---

 sha1_file.c |    8 ++++++++
 1 files changed, 8 insertions(+), 0 deletions(-)

4f388cf48271ec1547817a407ef1afe432cd59d4
diff --git a/sha1_file.c b/sha1_file.c
index 20f6419..65407e2 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -83,7 +83,15 @@ int safe_create_leading_directories(char
 			break;
 		*pos = 0;
 		if (mkdir(path, 0777) < 0) {
+#if !defined(ENOSYS)
 			if (errno != EEXIST) {
+#else
+			if (errno != EEXIST && errno != ENOSYS) {
+#endif
+/* 
+ * Solaris 8 appears to return ENOSYS when mkdir is applied 
+ * to an automount.
+ */
 				*pos = '/';
 				return -1;
 			}
-- 
1.1.6.g7159

^ permalink raw reply related

* Re: Two ideas for improving git's user interface
From: Junio C Hamano @ 2006-02-02  0:38 UTC (permalink / raw)
  To: Carl Worth; +Cc: git, Nicolas Pitre, Linus Torvalds
In-Reply-To: <87lkwupsbr.wl%cworth@cworth.org>

Carl Worth <cworth@cworth.org> writes:

> To explain this, "just use -a" isn't enough, it would have to be
> something like, "always use -a or else 'git commit' just won't work
> and you can end up committing stale garbage". And perhaps "unless you
> also add the filename to the commit line, then it will start working
> again."

I do not think you have to make it sound *that* negative.  I
agree it may be counterintuitive until the user groks the index.

Let's assume that we will fix things to (1) require "--also" (or
"--incremental") to get the current "git commit paths..."
behaviour, (2) without any arguments we commit the index as is,
(3) with explicit paths we commit clean HEAD plus only specified
paths using a temporary index.  I think a fairer way to say what
you said would be:

        Always use -a, or explicit paths.  With -a all of your
        changes in the working tree are committed.  With paths,
        only changes to those paths are committed.

        Once you are comfortable with making commits this way,
        you might want to learn about index file and then start
        using 'git commit' without any argument.  This works in
        a way that cannot be understood until you learn how the
        index file works, so stick to "-a or explicit paths"
        rule for now.  That rule is good enough for everyday
        use.

And you can probably go a long way without ever knowing about
index.  Initially when I wrote the above two paragraphs, I said
"appreciated" instead of "understood".  But depending on your
workflow, you may not even need what "git commit" without
arguments would give you, in which case there is nothing to
appreciate about, so I changed the wording.

Old-timer git people seem to like what it gives them but that
does not mean everybody should marvel at what it does and adopt
the workflow to take advantage of the index file.

> Here's a fundamental question I have, (and thanks to Keith Packard for
> helping me to phrase it):
>
> 	Is it ever useful (reasonable, desirable) to commit file
> 	contents that differ from the contents of the working
> 	directory?

What that means is people should always do "git commit -a".  Not
even "git commit paths...".  It matches _my_ sense of developer
discipline, especially for individual developers, but it is a
rather cumbersome straightjacket if enforced upon you in
practice.  It is a useful timesaver to be able to leave
unrelated changes around in the working tree.

> I don't think it is, (but please let me know if I've missed some
> useful case).

I think I've already done this a couple of times today.

Your "git diff" is interesting, but I'd rather make them
completely separate command from "git diff".  Perhaps "git
ndiff" and "git ncommit", that assumes there is nothing but "git
commit -a" kind of commits.

^ permalink raw reply

* Re: Question around git-shell usage in Everyday Git
From: Junio C Hamano @ 2006-02-02  0:04 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: git
In-Reply-To: <Pine.LNX.4.64.0602011530530.21884@g5.osdl.org>

Linus Torvalds <torvalds@osdl.org> writes:

> On Wed, 1 Feb 2006, Junio C Hamano wrote:
>> 
>> Do you mean to reuse single directory /home/gitu/ for user A, B, C,
>> and hang repositories /home/gitu/{X,Y,Z} for projects?  I'd
>> imagine things could be arranged that way.  User A and B but not
>> C may be in "projectX" group and /home/gitu/X is writable only
>> by projectX group members and such...
>
> That would work from a _git_ angle, but I don't think that was what Alan 
> was talking about, and it would failr horribly from a "ssh" perspective 
> (because "ssh" will want $HOME/.ssh/authorized_keys etc).

Hmph.  I thought that was what Alan was talking about, after he
read about the shared repository configuration section that
mentions git-shell, which is about shared repository.

Project administrator or project secretary sets up the shared
repository for the project and member accounts on that machine,
and members just use that shared repository.  Not allowing
remote repository creation nor deletion over git-shell is a
slightly lesser issue in this context.

> However, to be truly useful for this kind of hosting schenario, 
> "git-shell" still needs to be extended to be able to at a minimum create 
> (and delete) projects.

Hosting is a different story and I think branch/tag removal in
addition to repository creation, and deletion you listed above
are needed in that context.  Your "my projects are all under my
HOME directory, it is very convenient and things naturally work"
would apply for non-shared (i.e. something each person can call
"my") repositories, and would apply to hosting situation, of
course.

> It probably also makes a lot of sense to be able to pre-populate a 
> project, so that you don't have to do a remote "git push" to push a big 
> project over the network, when another version of that project already 
> exists at the hosting site.

Depends on what you mean by pre-populate, but that may just mean
to be able to manage objects/info/alternates remotely.
Capability to manage hook scripts may also be needed to a
certain degree if the repository side access control (e.g. who
can push into this branch) is done by hooks/pre-update, but in
either the hosting environment or a shared project setup, the
machine owner who is paranoid to install git-shell would want to
control what hooks can do very carefully.  The operator would
probably not allow updating of hooks at all, but install a BCP
hook by Carl Baldwin.  The users can only manage what is in the
info/allowed-{users,groups} file.

What this implies is extending what git-shell allows is a
concious policy decision and probably be quite different from
site to site until a set of BCP emerges.

^ permalink raw reply

* Re: [PATCH] git-svnimport.perl: fix for 'arg list too long...'
From: Matthias Urlichs @ 2006-02-01 23:50 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Sasha Khapyorsky, git
In-Reply-To: <7v3bj27kxe.fsf@assigned-by-dhcp.cox.net>

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

Hi,

Junio C Hamano:
> Smurf, I do not have problems with Sasha's patch.  Are you OK
> if I apply it?
> 
Go ahead.

-- 
Matthias Urlichs   |   {M:U} IT Design @ m-u-it.de   |  smurf@smurf.noris.de
Disclaimer: The quote was selected randomly. Really. | http://smurf.noris.de
 - -
Circumstances can force a generalized incompetent to become competent, at
least in a specialized field.
					-- Frank R. Freemon

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

^ permalink raw reply

* Re: Question around git-shell usage in Everyday Git
From: Linus Torvalds @ 2006-02-01 23:37 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Alan Chandler, git
In-Reply-To: <7vy80u64xf.fsf@assigned-by-dhcp.cox.net>



On Wed, 1 Feb 2006, Junio C Hamano wrote:
> 
> Do you mean to reuse single directory /home/gitu/ for user A, B, C,
> and hang repositories /home/gitu/{X,Y,Z} for projects?  I'd
> imagine things could be arranged that way.  User A and B but not
> C may be in "projectX" group and /home/gitu/X is writable only
> by projectX group members and such...

That would work from a _git_ angle, but I don't think that was what Alan 
was talking about, and it would failr horribly from a "ssh" perspective 
(because "ssh" will want $HOME/.ssh/authorized_keys etc).

So I _think_ what Alan was describing was just a 

	/home/user-A/project.git
		     another-project.git
		     ya-project.git
	      user-B/myproject.git
	      user-C/..

setup, and then people can always just describe their own projects by just 
doing "git pull host:<projectname>", without ever caring _where_ their 
home directory is.

(And yes, "git pull user@host:project" also obviously works fine, in case 
your user name at the hosting site isn't the same as the user name on your 
local site).

However, to be truly useful for this kind of hosting schenario, 
"git-shell" still needs to be extended to be able to at a minimum create 
(and delete) projects.

It probably also makes a lot of sense to be able to pre-populate a 
project, so that you don't have to do a remote "git push" to push a big 
project over the network, when another version of that project already 
exists at the hosting site.

			Linus

^ permalink raw reply

* Two ideas for improving git's user interface
From: Carl Worth @ 2006-02-01 23:33 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Junio C Hamano, Nicolas Pitre, git
In-Reply-To: <Pine.LNX.4.64.0602011433290.21884@g5.osdl.org>

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

On Wed, 1 Feb 2006 14:35:33 -0800 (PST), Linus Torvalds wrote:
>
> Agreed. However, I think that one is pretty easy to explain, and 
> conceptually it's not a problem to just tell people to use the "-a" flag 
> if they want to get CVS/SVN semantics.

"Just use -a" is tempting, but I don't think it's a satisfying stance
to take.

Consider the following operations:

	echo "original" > A; git add A; echo "modified" > A;
	git commit -a -m "add A";

	echo "original" > B; git add B; echo "modified" > B;
	git commit -m "add B" B;

	echo "original" > C; git add C; echo "modified" > C;
	git commit -m "add C";

After which we can see:

	$ git diff
	diff --git a/C b/C
	index 4b48dee..2e09960 100644
	--- a/C
	+++ b/C
	@@ -1 +1 @@
	-original
	+modified

To explain this, "just use -a" isn't enough, it would have to be
something like, "always use -a or else 'git commit' just won't work
and you can end up committing stale garbage". And perhaps "unless you
also add the filename to the commit line, then it will start working
again."

There's explanation for the above behavior requires a rather careful
description of the index, the operations, the flags, and some rather
subtle interactions between them.

I don't even think "embrace the index" is enough to make the above
behavior obvious---the variations in the above behavior are a bit too
subtle. 

But I don't think git is doomed to be hard to learn or that its
behavior needs to be hard to predict. I think this should be fairly
easy to fix.

Here's a fundamental question I have, (and thanks to Keith Packard for
helping me to phrase it):

	Is it ever useful (reasonable, desirable) to commit file
	contents that differ from the contents of the working
	directory?

I don't think it is, (but please let me know if I've missed some
useful case).

Idea #1 (prevent the index from being used to commit stale data)
-------
If this isn't useful, then I think git would do well to make it
harder/impossible to perform this operation. For example, the index
could have a new notion of "use working directory contents" for a
given file in addition to the current "use this blob". This would
allow a user to use the index to stage subsequent file
additions/modifications for commit without introducing the various
opportunities for confusing commits of stale data.

I would think this would then naturally resolve the confusion around
the various diff operations, (diff-index, diff-index --cached, and
diff-files).

Idea #2 (make it easy to preview diffs of what will be committed)
-------
Independent of the above, I'd like to propose another change to help
prevent confusion and to help users learn git. There should be an
obvious "diff" operation that presents exactly the result of what any
"commit" operation will perform.

I assume that there currently exist appropriate diff operations for
any commit, but the correspondence certainly isn't obvious. For
example, the simplest of commit commands:

	git commit

seems to correspond to a rather complex diff command (which I may not
have completely correct yet---and if not, that would just demonstrate
the point even more):

	git diff-index -p --cached HEAD

What I would love to have is the ability to pass the same arguments to
git diff to get a preview of what any get commit would do. For
example, something like:

	git diff		# would be a preview of:
	git commit

	git diff -a		# would be a preview of:
	git commit -a

	git diff fileA fileB	# would be a preview of:
	git commit fileA fileB

etc.

Again, thanks for your consideration to these thoughts from woefully
clueless and inexperienced user.

-Carl

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

^ permalink raw reply

* Re: Question around git-shell usage in Everyday Git
From: Linus Torvalds @ 2006-02-01 23:30 UTC (permalink / raw)
  To: Alan Chandler; +Cc: git
In-Reply-To: <200602012301.56141.alan@chandlerfamily.org.uk>



On Wed, 1 Feb 2006, Alan Chandler wrote:
> 
> My question, in this case is there any reason (such as git creating home 
> directoriy temp files or something) why each developer could not have their 
> home directory as the root of all the repositories (ie where my git user in 
> the above example had its home directory).

No reason why not.

I use my home directory for all _my_ projects, and just clone them within 
my own network with

	git pull g5:v2.6/linux ..

rather than using the absolute path-name.

And no, git should never use your home directory for anything else (ie 
all file operations are normally done just in $GIT_OBJECT_DIR or 
similar: some things like "git-diff-files" will use the current working 
directory, but nothing uses $HOME (*)).

So I would indeed suggest that the home directory would be the natural 
place to put developer projects.

		Linus

(*) Not entirely true. "git-cvsimport" uses "$HOME/.cvspass" for CVS 
passwords. But the _basic_ git commands shouldn't do that.

^ 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