Git development
 help / color / mirror / Atom feed
* Re: [PATCH 5/9] Add tests for git hash-object
From: Junio C Hamano @ 2007-10-26 21:00 UTC (permalink / raw)
  To: Adam Roben; +Cc: git, Johannes Sixt
In-Reply-To: <1193307927-3592-6-git-send-email-aroben@apple.com>

Adam Roben <aroben@apple.com> writes:

> +test_expect_success \
> +    'hash a file' \
> +    "test $hello_sha1 = \$(git hash-object hello)"
> +
> +test_expect_success \
> +    'hash from stdin' \
> +    "test $hello_sha1 = \$(echo '$hello_content' | git hash-object --stdin)"

Needs to make sure no object has been written to the object
database at this point?

> +test_expect_success \
> +    'hash a file and write to database' \
> +    "test $hello_sha1 = \$(git hash-object -w hello)"

... and make sure the objectis written here?

> +test_expect_success \
> +    'hash from stdin and write to database' \
> +    "test $hello_sha1 = \$(echo '$hello_content' | git hash-object -w --stdin)"
> +
> +test_done

... and/or here?

^ permalink raw reply

* Re: [PATCH 4/9] git-cat-file: Add --stdin option
From: Junio C Hamano @ 2007-10-26 20:59 UTC (permalink / raw)
  To: Adam Roben; +Cc: git, Brian Downing
In-Reply-To: <1193307927-3592-5-git-send-email-aroben@apple.com>

Adam Roben <aroben@apple.com> writes:

> @@ -23,6 +23,10 @@ OPTIONS
>  	For a more complete list of ways to spell object names, see
>  	"SPECIFYING REVISIONS" section in gitlink:git-rev-parse[1].
>  
> +--stdin::
> +	Read object names from stdin instead of specifying one on the
> +	command line.
> +

This does not talk about modified output format: what the format
is, nor when that modified format is used.

> @@ -139,16 +139,26 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
>  	if (!buf)
>  		die("git-cat-file %s: bad file", obj_name);
>  
> +	if (print_size) {
> +		printf("%lu\n", size);
> +		fflush(stdout);
> +	}
>  	write_or_die(1, buf, size);
> +	if (print_size) {
> +		printf("\n");
> +		fflush(stdout);
> +	}
>  	return 0;
>  }
>  

Not that I object strongly to it, but do we need extra LF after
the contents?

  - "It would help readers written in typical scripting
    languages" is an acceptable answer, but I doubt that is the
    case --- the reader is given the number of bytes and is
    going to "read($pipe, $buf, $that_size)" anyway.

  - "The reader can assert that one-byte past the content is a
    LF to catch errors, and this LF would help re-synchronize
    after such an error" would be another acceptable answer, but
    for the re-synchronization to work, the output needs to tell
    which record each chunk is about (i.e. if the output were
    "<type> <sha1> <size>LF<contents>LF", the "re-sync" argument
    would make a bit more sense).

> +	print_size = !opt || opt == 'p';

Needs a bit of comment here, and in the documentation.  E.g.

	git-cat-file --stdin -t <list-of-sha1
        git-cat-file --stdin -s <list-of-sha1

	are ways to check types and sizes of the objects in the
	list.

How does --stdin interact with -e?

How does --stdin interact with -p when printing a tree or a tag
object?

How does "blob --stdin" do when input sequence contains a non
blob SHA1?

It almost feels that --stdin should be named something else,
such as --batch or --bulk, as it is not just affecting the
input.

Here is an alternative suggestion.

   Two new options, --batch and --batch-check, are introduced.
   These options are incompatible with -[tsep] or an object type
   given as the first parameter to git-cat-file.

   * git-cat-file --batch-check <list-of-sha1

     outputs a record of this form

          <sha1> SP <type> SP <size> LF

     for each of the input lines.

   * git-cat-file --batch <list-of-sha1

     outputs a record of this form

          <sha1> SP <type> SP <size> LF <contents> LF

     for each of the input lines.

  For a missing object, either option gives a record of form:

          <sha1> SP missing LF

^ permalink raw reply

* Re: [PATCH 3/9] git-cat-file: Make option parsing a little more flexible
From: Junio C Hamano @ 2007-10-26 20:56 UTC (permalink / raw)
  To: Adam Roben; +Cc: git
In-Reply-To: <1193307927-3592-4-git-send-email-aroben@apple.com>

Adam Roben <aroben@apple.com> writes:

> This will make it easier to add newer options later.

A good change in principle.

> diff --git a/builtin-cat-file.c b/builtin-cat-file.c
> index 34a63d1..3a0be4a 100644
> --- a/builtin-cat-file.c
> +++ b/builtin-cat-file.c
> @@ -143,23 +143,41 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
>  	return 0;
>  }
>  
> +static const char cat_file_usage[] = "git-cat-file [-t|-s|-e|-p|<type>] <sha1>";
> +
>  int cmd_cat_file(int argc, const char **argv, const char *prefix)
>  {
> -	int opt;
> -	const char *exp_type, *obj_name;
> +	int i, opt = 0;
> +	const char *exp_type = 0, *obj_name = 0;

NULL pointer constants in git sources are spelled "NULL", not
"0".

^ permalink raw reply

* Re: best git practices, was Re: Git User's Survey 2007 unfinishedsummary continued
From: David Kastrup @ 2007-10-26 20:01 UTC (permalink / raw)
  To: Federico Mena Quintero; +Cc: Theodore Tso, git
In-Reply-To: <1193335339.4522.398.camel@cacharro.xalalinux.org>

Federico Mena Quintero <federico@novell.com> writes:

> You don't find quantum physicists saying, "... yeah, like Newton's
> brain-damaged followers" :)

Oh, one does get this sort of comments.  Predominantly from those who
understand neither theory.

-- 
David Kastrup, Kriemhildstr. 15, 44793 Bochum

^ permalink raw reply

* Re: git-svnimport
From: Junio C Hamano @ 2007-10-26 19:30 UTC (permalink / raw)
  To: Steven Grimm; +Cc: Johannes Schindelin, Gerrit Pape, git
In-Reply-To: <47222CB3.9070100@midwinter.com>

Steven Grimm <koreth@midwinter.com> writes:

> Johannes Schindelin wrote:
>>> I'm not sure these are worth fixing, I'd rather suggest to drop
>>> git-svnimport in favor of git-svn, and not installing it in future
>>> versions.
>>>
>> I already proposed this.  The outcome was... silent.
>>
>
> Me too, and same reaction.
>
> So I vote we interpret that as, "No objections from anyone."

Likewise.

^ permalink raw reply

* [StGit RFC] A more structured way of calling git
From: Karl Hasselström @ 2007-10-26 19:24 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: David Kågedal, Git Mailing List, Yann Dirson

I wanted to build an StGit command that coalesced adjacent patches to
a single patch. Because the end result tree would still be the same,
this should be doable without ever involving HEAD, the index, or the
worktree. StGit's existing infrastructure for manipulating patches
didn't lend itself to doing this kind of thing, though: it's not
modular enough. So I started to design a replacement low-level
interface to git, and things got slightly out of hand ... and I ended
up with a much bigger refactoring than I'd planned.

It's all outlined below, and all the code I currently have is
attached. Unless there's opposition, my plan is to convert one command
at a time to use the new infrastructure -- this can be done since the
on-disk format is unaffected.

Comments?


Python wrapping of git objects (gitlib.py)
----------------------------------------------------------------------

To make it easier to work with git objects, I've built a more
high-level interface than just calling git commands directly. Some of
it is trivial:

  * Blobs and tags aren't covered (yet), since StGit never uses them.
    I think. If they are needed in the future, they can easily be
    wrapped.

  * Trees are represented by the Python class Tree. They are
    immutable, and have only one property: the sha1 of the tree. More
    could be added if we need to look inside trees.

The interesting case is for commit objects:

  * Commits are represented by the Python class Commit. They are
    immutable, and have two properties: sha1 and commit data.

  * Commit data is represented by the Python class CommitData. These
    objects are also immutable, and have properties for author,
    committer, commit message, tree, and list of parent commits. They
    also have setter functions for these properties, which (since
    CommitData objects are immutable) return a modified copy of the
    original object.

  * Author and committer are represented by a Person class, also
    immutable with setter functions.

The user may create new CommitData objects, but never creates Tree or
Commit objects herself. Instead, she asks her Repository object to
create them for her:

  * Repository.get_tree and Repository.get_commit take sha1
    parameters, and returns the corresponding objects. They must
    already exist.

  * Repository.commit takes a CommitData parameter, and returns a
    corresponding Commit object representing the new commit.
    Internally, it runs git-commit-tree.

This has the nice property that Tree and Commit objects always
represent objects that git knows about. It also makes it trivial to
create new commits that have arbitrary existing commits as parents and
an existing tree. For example, my coalesce-adjacent-patches command
could be built on top of this.

The Repository object also has methods for reading and writing (and
caching) refs, but it lacks any method for creating new trees. This is
the job of the Index object. It has read_tree and write_tree methods
for getting Trees in to and out of the current index state. It also
has a merge method that does a 3-way merge between arbitrary Trees,
without ever touching the worktree (if the merge cannot be resolved
automatically, it simply fails).

The user is free to create as many Repository and Index objects as she
wants; their constructors take a git repository path and an index file
path as argumentes, respectively. This means that it's very easy to
work with a temporary index, which is neat in combination with the
Index.merge method: it lets you merge three trees to create a fourth
without ever touching the worktree or the default index.

For operations that involve a worktree as well, we have the
IndexAndWorktree class. It has methods for e.g. checkout and
update-index; this is also where a full, possibly conflicting merge
will go when I get around to implementing it.


Low-level StGit on top of the git wrappers (stacklib.py)
----------------------------------------------------------------------

That was all about git. We need an StGit layer on top of it.

There's a Stack object that represents a branch. It has two important
properties:

  * A PatchOrder object. This keeps track of the list of applied and
    unapplied patches, by name.

  * A store of Patch objects. This can look up Patch objects by name,
    and create new patches.

Patch objects represent patches, and are very simple. Basically the
only thing you can do with them is get their commit object, set their
commit object, and delete them. Author, commit message, top and
bottom, and all those things aren't a property of the patch; they are
properties of its commit.

(In the future, Patch objects should write stuff to the patch log.
They could also during a gradual transition to this new infrastructure
write out the per-patch metadata files that StGit currently uses.)

Importantly, unlike the current StGit stack class, there are no
high-level stack operations à la push and pop here. This is all
low-level manipulation of patch refs and the applied/unapplied files.
But in combination with the stuff in gitlib.py, lots of higher-level
StGit operations can be built on top of this.


Transactions (translib.py)
----------------------------------------------------------------------

I started to implement a few StGit commands on top of gitlib.py and
stacklib.py, and then realized something very appealing:

  Just about every StGit command can be accomplished by first creating
  a bunch of new commit objects with gitlib.py, then trying to check
  out the new HEAD, and then rewriting refs with stacklib.py. Only the
  first and second steps can possibly fail, and if they do, they do so
  without leaving any user-visible junk behind. This can be used to
  make all commands either succeed completely, or do nothing at all.

As an example (which I've not yet implemented), consider how push
would work:

  1. Create the new commit objects that the patches to be pushed will
     use. For each patch:

       a. Check if it's a fast forward. If so, just reuse the old
          commit object.

       b. Try the in-index merge with a temp index. If it succeeds,
          create a new commit object with that tree.

       c. Otherwise, stop trying to push any more patches.

  2. Check out the new HEAD tree. This may fail if the worktree and/or
     index contain conflicting changes. If so, we just abort the whole
     operation and tell the user which files she needs to clean up.

       a. If we had a patch that we couldn't push in (1.c), and then
          do a full 3-way merge with its original tree. This may fail
          if the worktree and/or index is dirty; if so, we don't try
          to push that patch.

       b. If the merge succeeds but with conflicts, create a new
          commit for it with the same tree as its parent (i.e. an
          empty commit) and leave the conflicts for the user to
          resolve.

       c. Otherwise, the merge autoresolved. Go back to (1) and try to
          push the remaining patches too. But remember that if we
          later need to abort the push due to dirty worktree/index, we
          have already pushed a few of the patches.

  3. Use stacklib.py to rewrite the branch ref and the patch refs.

This will end up pushing some subset of the requested patches. The
only way we'll ever get a result that isn't all-or-nothing is if a
merge conflicts. Note also how (except for the irritating (2.c)) we
never touch the index and worktree until we're already done, which
should make things both robust and fast.

(Step (2.c) is irritating, in that we actually have to check out a new
tree in order to use merge-recursive, and merge-recursive might
autoresolve a merge that the in-index merge failed to resolve, so that
we have checked out an intermediate tree even though there didn't end
up being any conflict for the user to resolve.)

The code in translib.py is a simple class that can hold a record of
everything that needs to be done in step (3), and then does it when
and if we get there.

The killer feature of transactions (apart from their use as a utility
when writing commands) is that we could build transaction logging.
Since every StGit command performs exatly one transaction, if we
simply logged the before and after values of the patch refs, branch
ref, and patch appliedness, we could build a generic StGit undo/redo
command.


Example commands (utillib.py)
----------------------------------------------------------------------

This file has sample implementations of some StGit commands: clean,
pop, push, and refresh. They don't have any bells and whistles, and
the push is fundamentally limited in that it doesn't handle conflicts
-- it'll complain and do nothing.

These were mostly done to excercise the new infrastructure and make
sure that I hadn't forgotten anything. The plan is not to replace the
existing commands, just make them use the new infrastructure.




diff --git a/stgit/gitlib.py b/stgit/gitlib.py
new file mode 100644
index 0000000..46911d5
--- /dev/null
+++ b/stgit/gitlib.py
@@ -0,0 +1,360 @@
+import os, os.path, re
+from exception import *
+import run
+
+class DetachedHeadException(StgException):
+    pass
+
+class Repr(object):
+    def __repr__(self):
+        return str(self)
+
+class NoValue(object):
+    pass
+
+def make_defaults(defaults):
+    def d(val, attr):
+        if val != NoValue:
+            return val
+        elif defaults != NoValue:
+            return getattr(defaults, attr)
+        else:
+            return None
+    return d
+
+class Person(Repr):
+    """Immutable."""
+    def __init__(self, name = NoValue, email = NoValue,
+                 date = NoValue, defaults = NoValue):
+        d = make_defaults(defaults)
+        self.__name = d(name, 'name')
+        self.__email = d(email, 'email')
+        self.__date = d(date, 'date')
+    name = property(lambda self: self.__name)
+    email = property(lambda self: self.__email)
+    date = property(lambda self: self.__date)
+    def set_name(self, name):
+        return type(self)(name = name, defaults = self)
+    def set_email(self, email):
+        return type(self)(email = email, defaults = self)
+    def set_date(self, date):
+        return type(self)(date = date, defaults = self)
+    def __str__(self):
+        return '%s <%s> %s' % (self.name, self.email, self.date)
+    @classmethod
+    def parse(cls, s):
+        m = re.match(r'^([^<]*)<([^>]*)>\s+(\d+\s+[+-]\d{4})$', s)
+        assert m
+        name = m.group(1).strip()
+        email = m.group(2)
+        date = m.group(3)
+        return cls(name, email, date)
+
+class Tree(Repr):
+    """Immutable."""
+    def __init__(self, sha1):
+        self.__sha1 = sha1
+    sha1 = property(lambda self: self.__sha1)
+    def __str__(self):
+        return 'Tree<%s>' % self.sha1
+
+class Commitdata(Repr):
+    """Immutable."""
+    def __init__(self, tree = NoValue, parents = NoValue, author = NoValue,
+                 committer = NoValue, message = NoValue, defaults = NoValue):
+        d = make_defaults(defaults)
+        self.__tree = d(tree, 'tree')
+        self.__parents = d(parents, 'parents')
+        self.__author = d(author, 'author')
+        self.__committer = d(committer, 'committer')
+        self.__message = d(message, 'message')
+    tree = property(lambda self: self.__tree)
+    parents = property(lambda self: self.__parents)
+    @property
+    def parent(self):
+        assert len(self.__parents) == 1
+        return self.__parents[0]
+    author = property(lambda self: self.__author)
+    committer = property(lambda self: self.__committer)
+    message = property(lambda self: self.__message)
+    def set_tree(self, tree):
+        return type(self)(tree = tree, defaults = self)
+    def set_parents(self, parents):
+        return type(self)(parents = parents, defaults = self)
+    def add_parent(self, parent):
+        return type(self)(parents = list(self.parents or []) + [parent],
+                          defaults = self)
+    def set_parent(self, parent):
+        return self.set_parents([parent])
+    def set_author(self, author):
+        return type(self)(author = author, defaults = self)
+    def set_committer(self, committer):
+        return type(self)(committer = committer, defaults = self)
+    def set_message(self, message):
+        return type(self)(message = message, defaults = self)
+    def __str__(self):
+        if self.tree == None:
+            tree = None
+        else:
+            tree = self.tree.sha1
+        if self.parents == None:
+            parents = None
+        else:
+            parents = [p.sha1 for p in self.parents]
+        return ('Commitdata<tree: %s, parents: %s, author: %s,'
+                ' committer: %s, message: "%s">'
+                ) % (tree, parents, self.author, self.committer, self.message)
+    @classmethod
+    def parse(cls, repository, s):
+        cd = cls()
+        lines = list(s.splitlines(True))
+        for i in xrange(len(lines)):
+            line = lines[i].strip()
+            if not line:
+                return cd.set_message(''.join(lines[i+1:]))
+            key, value = line.split(None, 1)
+            if key == 'tree':
+                cd = cd.set_tree(repository.get_tree(value))
+            elif key == 'parent':
+                cd = cd.add_parent(repository.get_commit(value))
+            elif key == 'author':
+                cd = cd.set_author(Person.parse(value))
+            elif key == 'committer':
+                cd = cd.set_committer(Person.parse(value))
+            else:
+                assert False
+        assert False
+
+class Commit(Repr):
+    """Immutable."""
+    def __init__(self, repository, sha1):
+        self.__sha1 = sha1
+        self.__repository = repository
+        self.__data = None
+    sha1 = property(lambda self: self.__sha1)
+    @property
+    def data(self):
+        if self.__data == None:
+            self.__data = Commitdata.parse(
+                self.__repository,
+                self.__repository.cat_object(self.sha1))
+        return self.__data
+    def __str__(self):
+        return 'Commit<sha1: %s, data: %s>' % (self.sha1, self.__data)
+
+class Refs(object):
+    def __init__(self, repository):
+        self.__repository = repository
+        self.__refs = None
+    def __cache_refs(self):
+        self.__refs = {}
+        for line in self.__repository.run(['git-show-ref']).output_lines():
+            m = re.match(r'^([0-9a-f]{40})\s+(\S+)$', line)
+            sha1, ref = m.groups()
+            self.__refs[ref] = sha1
+    def get(self, ref):
+        if self.__refs == None:
+            self.__cache_refs()
+        return self.__repository.get_commit(self.__refs[ref])
+    def set(self, ref, commit, msg):
+        if self.__refs == None:
+            self.__cache_refs()
+        old_sha1 = self.__refs.get(ref, '0'*40)
+        new_sha1 = commit.sha1
+        if old_sha1 != new_sha1:
+            self.__repository.run(['git-update-ref', '-m', msg,
+                                   ref, new_sha1, old_sha1]).no_output()
+            self.__refs[ref] = new_sha1
+    def delete(self, ref):
+        if self.__refs == None:
+            self.__cache_refs()
+        self.__repository.run(['git-update-ref',
+                               '-d', ref, self.__refs[ref]]).no_output()
+        del self.__refs[ref]
+
+class ObjectCache(object):
+    def __init__(self, create):
+        self.__objects = {}
+        self.__create = create
+    def __getitem__(self, name):
+        if not name in self.__objects:
+            self.__objects[name] = self.__create(name)
+        return self.__objects[name]
+    def __contains__(self, name):
+        return name in self.__objects
+    def __setitem__(self, name, val):
+        assert not name in self.__objects
+        self.__objects[name] = val
+
+def add_dict(d1, d2):
+    d = dict(d1)
+    d.update(d2)
+    return d
+
+class RunWithEnv(object):
+    def run(self, args, env = {}):
+        return run.Run(*args).env(add_dict(self.env, env))
+
+class Repository(RunWithEnv):
+    def __init__(self, directory):
+        self.__git_dir = directory
+        self.__refs = Refs(self)
+        self.__trees = ObjectCache(lambda sha1: Tree(sha1))
+        self.__commits = ObjectCache(lambda sha1: Commit(self, sha1))
+    env = property(lambda self: { 'GIT_DIR': self.__git_dir })
+    @classmethod
+    def default(cls):
+        """Return the default repository."""
+        return cls(run.Run('git-rev-parse', '--git-dir').output_one_line())
+    def default_index(self):
+        return Index(self, (os.environ.get('GIT_INDEX_FILE', None)
+                            or os.path.join(self.__git_dir, 'index')))
+    def temp_index(self):
+        return Index(self, self.__git_dir)
+    def default_worktree(self):
+        path = os.environ.get('GIT_WORK_TREE', None)
+        if not path:
+            o = run.Run('git-rev-parse', '--show-cdup').output_lines()
+            o = o or ['.']
+            assert len(o) == 1
+            path = o[0]
+        return Worktree(path)
+    def default_iw(self):
+        return IndexAndWorktree(self.default_index(), self.default_worktree())
+    directory = property(lambda self: self.__git_dir)
+    refs = property(lambda self: self.__refs)
+    def cat_object(self, sha1):
+        return self.run(['git-cat-file', '-p', sha1]).raw_output()
+    def get_tree(self, sha1):
+        return self.__trees[sha1]
+    def get_commit(self, sha1):
+        return self.__commits[sha1]
+    def commit(self, commitdata):
+        c = ['git-commit-tree', commitdata.tree.sha1]
+        for p in commitdata.parents:
+            c.append('-p')
+            c.append(p.sha1)
+        env = {}
+        for p, v1 in ((commitdata.author, 'AUTHOR'),
+                       (commitdata.committer, 'COMMITTER')):
+            if p != None:
+                for attr, v2 in (('name', 'NAME'), ('email', 'EMAIL'),
+                                 ('date', 'DATE')):
+                    if getattr(p, attr) != None:
+                        env['GIT_%s_%s' % (v1, v2)] = getattr(p, attr)
+        sha1 = self.run(c, env = env).raw_input(commitdata.message
+                                                ).output_one_line()
+        return self.get_commit(sha1)
+    @property
+    def head(self):
+        try:
+            return self.run(['git-symbolic-ref', '-q', 'HEAD']
+                            ).output_one_line()
+        except run.RunException:
+            raise DetachedHeadException()
+    def set_head(self, ref, msg):
+        self.run(['git-symbolic-ref', '-m', msg, 'HEAD', ref]).no_output()
+    @property
+    def head_commit(self):
+        return self.get_commit(self.run(['git-rev-parse', 'HEAD']
+                                        ).output_one_line())
+
+class MergeException(StgException):
+    pass
+
+class Index(RunWithEnv):
+    def __init__(self, repository, filename):
+        self.__repository = repository
+        if os.path.isdir(filename):
+            # Create a temp index in the given directory.
+            self.__filename = os.path.join(
+                filename, 'index.temp-%d-%x' % (os.getpid(), id(self)))
+            self.delete()
+        else:
+            self.__filename = filename
+    env = property(lambda self: add_dict(self.__repository.env,
+                                         { 'GIT_INDEX_FILE': self.__filename }))
+    def read_tree(self, tree):
+        self.run(['git-read-tree', tree.sha1]).no_output()
+    def write_tree(self):
+        return self.__repository.get_tree(
+            self.run(['git-write-tree']).output_one_line())
+    def is_clean(self):
+        try:
+            self.run(['git-update-index', '--refresh']).discard_output()
+        except run.RunException:
+            return False
+        else:
+            return True
+    def merge(self, base, ours, theirs):
+        """In-index merge, no worktree involved."""
+        self.run(['git-read-tree', '-m', '-i', '--aggressive',
+                  base.sha1, ours.sha1, theirs.sha1]).no_output()
+        try:
+            self.run(['git-merge-index', 'git-merge-one-file', '-a']
+                     ).no_output()
+        except run.RunException:
+            raise MergeException('In-index merge failed due to conflicts')
+    def delete(self):
+        if os.path.isfile(self.__filename):
+            os.remove(self.__filename)
+
+class Worktree(object):
+    def __init__(self, directory):
+        self.__directory = directory
+    env = property(lambda self: { 'GIT_WORK_TREE': self.__directory })
+
+class CheckoutException(StgException):
+    pass
+
+class IndexAndWorktree(RunWithEnv):
+    def __init__(self, index, worktree):
+        self.__index = index
+        self.__worktree = worktree
+    index = property(lambda self: self.__index)
+    env = property(lambda self: add_dict(self.__index.env, self.__worktree.env))
+    def checkout(self, old_commit, new_commit):
+        # TODO: Optionally do a 3-way instead of doing nothing when we
+        # have a problem. Or maybe we should stash changes in a patch?
+        try:
+            self.run(['git-read-tree', '-u', '-m',
+                      '--exclude-per-directory=.gitignore',
+                      old_commit.sha1, new_commit.sha1]
+                     ).discard_output()
+        except run.RunException:
+            raise CheckoutException('Index/workdir dirty')
+    def changed_files(self):
+        return self.run(['git-diff-files', '--name-only']).output_lines()
+    def update_index(self, files):
+        self.run(['git-update-index', '--remove', '-z', '--stdin']
+                 ).input_nulterm(files).discard_output()
+
+if __name__ == '__main__':
+    testdir = '/tmp/stgtest'
+    os.system('rm -rf %s' % testdir)
+    os.makedirs(testdir)
+    os.chdir(testdir)
+    for c in ['git init',
+              'echo foo >> foo',
+              'git add foo',
+              'git commit -m foo',
+              'echo bar >> foo',
+              'git commit -a -m foo']:
+        os.system(c)
+    r = Repository(os.path.join(testdir, '.git'))
+    head = r.head
+    c = r.refs.get(head)
+    print 'HEAD is', head, 'which is', c
+    c.data
+    print 'Expanded:', c
+    maja = Person(name = 'Maja', email = 'maja@example.com')
+    nisse = Person(name = 'Nisse', email = 'nisse@example.com')
+    c2 = r.commit(c.data.set_parents([c]).set_author(maja))
+    c3 = r.commit(c.data.set_parents([c]).set_author(nisse))
+    c4 = r.commit(c.data.set_parents([c2, c3]))
+    r.refs.set(head, c4, 'made a cool merge')
+    c5 = r.commit(c.data.set_parents([c4]).set_tree(
+        c.data.parents[0].data.tree))
+    head = 'refs/heads/foobar'
+    r.refs.set(head, c5, 'committed a revert')
+    r.set_head(head, 'switched to other branch')
diff --git a/stgit/run.py b/stgit/run.py
index 924e59a..43c3a23 100644
--- a/stgit/run.py
+++ b/stgit/run.py
@@ -105,7 +105,7 @@ class Run:
     def input_lines(self, lines):
         self.__indata = ''.join(['%s\n' % line for line in lines])
         return self
-    def input_nulterm(self, items):
+    def input_nulterm(self, lines):
         self.__indata = ''.join('%s\0' % line for line in lines)
         return self
     def no_output(self):
diff --git a/stgit/stacklib.py b/stgit/stacklib.py
new file mode 100644
index 0000000..06ba007
--- /dev/null
+++ b/stgit/stacklib.py
@@ -0,0 +1,120 @@
+import os.path
+import gitlib, utils
+
+class Patch(object):
+    def __init__(self, stack, name):
+        self.__stack = stack
+        self.__name = name
+    name = property(lambda self: self.__name)
+    def __ref(self):
+        return 'refs/patches/%s/%s' % (self.__stack.name, self.__name)
+    @property
+    def commit(self):
+        return self.__stack.repository.refs.get(self.__ref())
+    def set_commit(self, commit, msg):
+        self.__stack.repository.refs.set(self.__ref(), commit, msg)
+    def delete(self):
+        self.__stack.repository.refs.delete(self.__ref())
+    def is_applied(self):
+        return self.name in self.__stack.patchorder.applied
+    def is_empty(self):
+        c = self.commit
+        return c.data.tree == c.data.parent.data.tree
+
+class PatchOrder(object):
+    """Keeps track of patch order, and which patches are applied.
+    Works with patch names, not actual patches."""
+    __list_order = [ 'applied', 'unapplied' ]
+    def __init__(self, stack):
+        self.__stack = stack
+        self.__lists = {}
+    def __read_file(self, fn):
+        return tuple(utils.read_strings(
+            os.path.join(self.__stack.directory, fn)))
+    def __write_file(self, fn, val):
+        utils.write_strings(os.path.join(self.__stack.directory, fn), val)
+    def __get_list(self, name):
+        if not name in self.__lists:
+            self.__lists[name] = self.__read_file(name)
+        return self.__lists[name]
+    def __set_list(self, name, val):
+        val = tuple(val)
+        if val != self.__lists.get(name, None):
+            self.__lists[name] = val
+            self.__write_file(name, val)
+    applied = property(lambda self: self.__get_list('applied'),
+                       lambda self, val: self.__set_list('applied', val))
+    unapplied = property(lambda self: self.__get_list('unapplied'),
+                         lambda self, val: self.__set_list('unapplied', val))
+
+class Patches(object):
+    """Creates Patch objects."""
+    def __init__(self, stack):
+        self.__stack = stack
+        def create_patch(name):
+            p = Patch(self.__stack, name)
+            p.commit # raise exception if the patch doesn't exist
+            return p
+        self.__patches = gitlib.ObjectCache(create_patch) # name -> Patch
+    def exists(self, name):
+        return name in self.__patches
+    def get(self, name):
+        return self.__patches[name]
+    def new(self, name, commit, msg):
+        assert not name in self.__patches
+        p = Patch(self.__stack, name)
+        p.set_commit(commit, msg)
+        self.__patches[name] = p
+        return p
+
+class Stack(object):
+    def __init__(self, repository, name):
+        self.__repository = repository
+        self.__name = name
+        self.__patchorder = PatchOrder(self)
+        self.__patches = Patches(self)
+    name = property(lambda self: self.__name)
+    repository = property(lambda self: self.__repository)
+    patchorder = property(lambda self: self.__patchorder)
+    patches = property(lambda self: self.__patches)
+    @property
+    def directory(self):
+        return os.path.join(self.__repository.directory, 'patches', self.__name)
+    def __ref(self):
+        return 'refs/heads/%s' % self.__name
+    @property
+    def head(self):
+        return self.__repository.refs.get(self.__ref())
+    def set_head(self, commit, msg):
+        self.__repository.refs.set(self.__ref(), commit, msg)
+    @property
+    def base(self):
+        if self.patchorder.applied:
+            return self.patches.get(self.patchorder.applied[0]
+                                    ).commit.data.parent
+        else:
+            return self.head
+
+def strip_leading(prefix, s):
+    assert s.startswith(prefix)
+    return s[len(prefix):]
+
+class Repository(gitlib.Repository):
+    def __init__(self, *args, **kwargs):
+        gitlib.Repository.__init__(self, *args, **kwargs)
+        self.__stacks = {} # name -> Stack
+    @property
+    def current_branch(self):
+        return strip_leading('refs/heads/', self.head)
+    @property
+    def current_stack(self):
+        return self.get_stack(self.current_branch)
+    def get_stack(self, name):
+        if not name in self.__stacks:
+            if name == None:
+                s = None # detached HEAD
+            else:
+                # TODO: verify that the branch exists
+                s = Stack(self, name)
+            self.__stacks[name] = s
+        return self.__stacks[name]
diff --git a/stgit/translib.py b/stgit/translib.py
new file mode 100644
index 0000000..deb3420
--- /dev/null
+++ b/stgit/translib.py
@@ -0,0 +1,70 @@
+import gitlib
+from exception import *
+from out import *
+
+class TransactionException(StgException):
+    pass
+
+class StackTransaction(object):
+    def __init__(self, stack, msg):
+        self.__stack = stack
+        self.__msg = msg
+        self.__patches = {}
+        self.__applied = list(self.__stack.patchorder.applied)
+        self.__unapplied = list(self.__stack.patchorder.unapplied)
+    def __set_patches(self, val):
+        self.__patches = dict(val)
+    patches = property(lambda self: self.__patches, __set_patches)
+    def __set_applied(self, val):
+        self.__applied = list(val)
+    applied = property(lambda self: self.__applied, __set_applied)
+    def __set_unapplied(self, val):
+        self.__unapplied = list(val)
+    unapplied = property(lambda self: self.__unapplied, __set_unapplied)
+    def __check_consistency(self):
+        remaining = set(self.__applied + self.__unapplied)
+        for pn, commit in self.__patches.iteritems():
+            if commit == None:
+                assert self.__stack.patches.exists(pn)
+            else:
+                assert pn in remaining
+    def run(self, iw = None):
+        self.__check_consistency()
+
+        # Get new head commit.
+        if self.__applied:
+            top_patch = self.__applied[-1]
+            try:
+                new_head = self.__patches[top_patch]
+            except KeyError:
+                new_head = self.__stack.patches.get(top_patch).commit
+        else:
+            new_head = self.__stack.base
+
+        # Set branch head.
+        if new_head == self.__stack.head:
+            out.info('Head remains at %s' % new_head.sha1[:8])
+        elif new_head.data.tree == self.__stack.head.data.tree:
+            out.info('Head %s -> %s (same tree)' % (self.__stack.head.sha1[:8],
+                                                    new_head.sha1[:8]))
+        elif iw != None:
+            try:
+                iw.checkout(self.__stack.head, new_head)
+            except gitlib.CheckoutException, e:
+                raise TransactionException(e)
+            out.info('Head %s -> %s' % (self.__stack.head.sha1[:8],
+                                        new_head.sha1[:8]))
+        self.__stack.set_head(new_head, self.__msg)
+
+        # Write patches.
+        for pn, commit in self.__patches.iteritems():
+            if self.__stack.patches.exists(pn):
+                p = self.__stack.patches.get(pn)
+                if commit == None:
+                    p.delete()
+                else:
+                    p.set_commit(commit, self.__msg)
+            else:
+                self.__stack.patches.new(pn, commit, self.__msg)
+        self.__stack.patchorder.applied = self.__applied
+        self.__stack.patchorder.unapplied = self.__unapplied
diff --git a/stgit/utillib.py b/stgit/utillib.py
new file mode 100644
index 0000000..d09ecc4
--- /dev/null
+++ b/stgit/utillib.py
@@ -0,0 +1,139 @@
+import gitlib, translib
+from out import *
+
+def head_top_equal(repository):
+    head = repository.head_commit
+    try:
+        s = repository.current_stack
+    except gitlib.DetachedHeadException:
+        out.error('Not on any branch (detached HEAD)')
+        return False
+    applied = s.patchorder.applied
+    if not applied:
+        return True
+    top = s.patches.get(applied[-1])
+    if top.commit == head:
+        return True
+    out.error('The top patch (%s, %s)' % (top.name, top.commit.sha1),
+              'and HEAD (%s) are not the same.' % head.sha1)
+    return False
+
+def simple_merge(repository, base, ours, theirs):
+    """Given three trees, tries to do an in-index merge in a temporary
+    index with a temporary index. Returns the result tree, or None if
+    the merge failed (due to conflicts)."""
+    assert isinstance(base, gitlib.Tree)
+    assert isinstance(ours, gitlib.Tree)
+    assert isinstance(theirs, gitlib.Tree)
+    if base == ours:
+        # Fast forward: theirs is a descendant of ours.
+        return theirs
+    if base == theirs:
+        # Fast forward: ours is a descendant of theirs.
+        return ours
+    index = repository.temp_index()
+    try:
+        try:
+            index.merge(base, ours, theirs)
+        except gitlib.MergeException:
+            return None
+        return index.write_tree()
+    finally:
+        index.delete()
+
+def clean(stack):
+    t = translib.StackTransaction(stack, 'stg clean')
+    t.unapplied = []
+    for pn in stack.patchorder.unapplied:
+        p = stack.patches.get(pn)
+        if p.is_empty():
+            t.patches[pn] = None
+        else:
+            t.unapplied.append[pn]
+    t.applied = []
+    parent = stack.base
+    for pn in stack.patchorder.applied:
+        p = stack.patches.get(pn)
+        if p.is_empty():
+            t.patches[pn] = None
+            out.info('Deleting %s' % pn)
+        else:
+            if parent != p.commit.data.parent:
+                parent = t.patches[pn] = stack.repository.commit(
+                    p.commit.data.set_parent(parent))
+            else:
+                parent = p.commit
+            t.applied.append(pn)
+            out.info('Keeping %s' % pn)
+    t.run()
+
+def pop(stack, iw = None):
+    t = translib.StackTransaction(stack, 'stg pop')
+    pn = t.applied.pop()
+    t.unapplied.insert(0, pn)
+    t.run(iw)
+
+def push(stack, pn, iw = None):
+    t = translib.StackTransaction(stack, 'stg push')
+    t.unapplied.remove(pn)
+    t.applied.append(pn)
+    p = stack.patches.get(pn)
+    if stack.head != p.commit.data.parent:
+        tree = simple_merge(stack.repository, p.commit.data.parent.data.tree,
+                            stack.head.data.tree, p.commit.data.tree)
+        assert tree
+        t.patches[pn] = stack.repository.commit(
+            p.commit.data.set_parent(stack.head).set_tree(tree))
+    t.run(iw)
+
+def refresh(stack, iw):
+    iw.update_index(iw.changed_files())
+    tree = iw.index.write_tree()
+    t = translib.StackTransaction(stack, 'stg refresh')
+    p = stack.patches.get(t.applied[-1])
+    t.patches[p.name] = stack.repository.commit(
+        p.commit.data.set_tree(tree))
+    t.run()
+
+if __name__ == '__main__':
+    import os
+    import stacklib
+    testdir = '/tmp/stgtest'
+    os.system('rm -rf %s' % testdir)
+    os.makedirs(testdir)
+    os.chdir(testdir)
+    for c in ['git init',
+              'echo foo >> foo',
+              'git add foo',
+              'git commit -m foo',
+              'stg init']:
+        os.system(c)
+    for i in range(3):
+        for c in ['stg new p%d -m p%d' % (i, i),
+                  'echo %s >> foo%d' % (str(i)*4, i),
+                  'git add foo%d' % i,
+                  'stg refresh',
+                  'stg new q%d -m q%d' % (i, i)]:
+            os.system(c)
+    r = stacklib.Repository.default()
+    print 'Current branch:', r.current_branch
+    s = r.current_stack
+    print 'Applied:', s.patchorder.applied
+    print 'Unapplied:', s.patchorder.unapplied
+    os.system('git checkout HEAD^')
+    head_top_equal(r)
+    os.system('git checkout master')
+    head_top_equal(r)
+    clean(s)
+    iw = r.default_iw()
+    pop(s, iw)
+    pop(s, iw)
+    os.system('stg series')
+    os.system('stg status')
+    push(s, 'p2', iw)
+    os.system('stg series')
+    os.system('stg status')
+    os.system('echo 333 >> foo0')
+    refresh(s, iw)
+    os.system('stg series')
+    os.system('stg status')

-- 
Karl Hasselström, kha@treskal.com
      www.treskal.com/kalle

^ permalink raw reply related

* Re: How to remove a specific hunk
From: Alex Riesen @ 2007-10-26 19:20 UTC (permalink / raw)
  To: Pascal Obry; +Cc: git
In-Reply-To: <4722036E.5030204@wanadoo.fr>

Pascal Obry, Fri, Oct 26, 2007 17:10:38 +0200:
> 
> I'm very new to Git... but start to love it :)
> 
> Before committing sometimes I want to remove a specific hunk. Say in
> file a.txt I have in the diff 3 hunks, I want to revert/delete/remove
> the second one. Is there a way to do that ?

Take a look at git-gui. Try right-clicking in the diff pane at the
bottom.

^ permalink raw reply

* Re: How to remove a specific hunk
From: Johannes Schindelin @ 2007-10-26 18:19 UTC (permalink / raw)
  To: Benoit SIGOURE; +Cc: Pascal Obry, git list
In-Reply-To: <2669F76D-6FF2-4CCF-9337-639D84EE65E8@lrde.epita.fr>

Hi,

On Fri, 26 Oct 2007, Benoit SIGOURE wrote:

> On Oct 26, 2007, at 5:38 PM, Pascal Obry wrote:
> 
> > Andreas,
> > 
> > > Once you've added the other two hunks, they'll no longer show up in
> > > git-diff, so you can do something like this:
> > > 
> > > $ git-add -i; # add the other two hunks to commit
> > > $ git-diff > middle-hunk.patch
> > > $ git-apply -R middle-hunk.patch
> > > test, test, test
> > > $ git-apply middle-hunk.patch
> > 
> > Thanks, this will clearly work. I was expecting something more
> > integrated like a "git reset --interactive" or something like that :)
> 
> That'd be great!  :)

I skipped over the beginnings of this thread because of time constraints, 
but would "git reset HEAD^ && git add -i" not helped you?  git add -i 
allows you to stage hunks, so by just _not_ staging _that_ hunk but 
everything else, should have worked for you, right?

There's also git-gui which does all that graphically for you (remember the 
right mouse button).

Ciao,
Dscho

^ permalink raw reply

* Re: git-svnimport
From: Steven Grimm @ 2007-10-26 18:06 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: Gerrit Pape, git
In-Reply-To: <Pine.LNX.4.64.0710261745110.4362@racer.site>

Johannes Schindelin wrote:
>> I'm not sure these are worth fixing, I'd rather suggest to drop 
>> git-svnimport in favor of git-svn, and not installing it in future 
>> versions.
>>     
> I already proposed this.  The outcome was... silent.
>   

Me too, and same reaction.

So I vote we interpret that as, "No objections from anyone."

-Steve

^ permalink raw reply

* Re: [PATCH] Fix regression in fast-import.c due to strbufs.
From: Pierre Habouzit @ 2007-10-26 17:25 UTC (permalink / raw)
  To: Junio C Hamano, Shun Kei Leung
  Cc: Johannes Schindelin, Shawn O. Pearce, Git ML
In-Reply-To: <20071026075912.GA25365@artemis.corp>

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

Dear Shun, the fast-import bug your reported has a fix.

Dear Junio, please merge the patch in the mail I'm answering to[0] into
master as it fixes a crash in fast-import.

Cheers,

  [0]  Message-Id: <20071026075912.GA25365@artemis.corp>

diff --git a/fast-import.c b/fast-import.c
index 6f888f6..f93d7d6 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -1616,6 +1616,7 @@ static void cmd_data(struct strbuf *sb)
 		char *term = xstrdup(command_buf.buf + 5 + 2);
 		size_t term_len = command_buf.len - 5 - 2;
 
+		strbuf_detach(&command_buf, NULL);
 		for (;;) {
 			if (strbuf_getline(&command_buf, stdin, '\n') == EOF)
 				die("EOF in data (terminator '%s' not found)", term);
-- 
1.5.3.4.1358.gfae55-dirty




-- 
·O·  Pierre Habouzit
··O                                                madcoder@debian.org
OOO                                                http://www.madism.org

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

^ permalink raw reply related

* Re: How to remove a specific hunk
From: Benoit SIGOURE @ 2007-10-26 17:03 UTC (permalink / raw)
  To: Pascal Obry; +Cc: git list
In-Reply-To: <47220A05.4040705@wanadoo.fr>

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

On Oct 26, 2007, at 5:38 PM, Pascal Obry wrote:

> Andreas,
>
>> Once you've added the other two hunks, they'll no longer show up in
>> git-diff, so you can do something like this:
>>
>> $ git-add -i; # add the other two hunks to commit
>> $ git-diff > middle-hunk.patch
>> $ git-apply -R middle-hunk.patch
>> test, test, test
>> $ git-apply middle-hunk.patch
>
> Thanks, this will clearly work. I was expecting something more
> integrated like a "git reset --interactive" or something like that :)

That'd be great!  :)

-- 
Benoit Sigoure aka Tsuna
EPITA Research and Development Laboratory



[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 186 bytes --]

^ permalink raw reply

* Re: [msysGit] Re: Fourth incarnation of the msysGit herald
From: Steffen Prohaska @ 2007-10-26 16:34 UTC (permalink / raw)
  To: Pascal Obry; +Cc: Jan Hudec, Johannes Schindelin, msysgit, git
In-Reply-To: <47220B54.60508@obry.net>


On Oct 26, 2007, at 5:44 PM, Pascal Obry wrote:

> The Cygwin/Git module is works very well. And Cygwin is definitely a
> nice toolset (X, bash, ssh, rsync, ...) when you are forced working on
> Windows :)

But beware of the textmode.

	Steffen

^ permalink raw reply

* Re: [msysGit] Re: Fourth incarnation of the msysGit herald
From: Pascal Obry @ 2007-10-26 16:56 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: Jan Hudec, Steffen Prohaska, msysgit, git
In-Reply-To: <Pine.LNX.4.64.0710261746380.4362@racer.site>

Johannes Schindelin a écrit :
> git-svn has nothing to do with external module support.

Ok, "external module" was not the proper term, sorry English is not my
main language. I should have said "conduit for changesets between
Subversion and Git".

> And remember, msysGit will fly sooner if people like _you_ help.

:) Of course, but I'm already doing lot of Open Source development,
can't do everything :)

Pascal.

-- 

--|------------------------------------------------------
--| Pascal Obry                           Team-Ada Member
--| 45, rue Gabriel Peri - 78114 Magny Les Hameaux FRANCE
--|------------------------------------------------------
--|              http://www.obry.net
--| "The best way to travel is by means of imagination"
--|
--| gpg --keyserver wwwkeys.pgp.net --recv-key C1082595

^ permalink raw reply

* Re: How to remove a specific hunk
From: Jeff King @ 2007-10-26 16:49 UTC (permalink / raw)
  To: Andreas Ericsson; +Cc: Pascal Obry, git
In-Reply-To: <20071026164207.GB19673@coredump.intra.peff.net>

On Fri, Oct 26, 2007 at 12:42:08PM -0400, Jeff King wrote:

> That has the side effect of changing the index state. My preferred
> method would be to just excise the hunk from the diff using an editor
> (and this actually has nothing to do with git; you could be using
> diff/patch):
> 
> git-diff file >patch
> # remove every hunk except what you want to cut out
> $EDITOR patch
> git-apply -R patch

BTW, since this is inherently a non-git operation, there are other tools
that some may find friendlier than an editor. Kompare will let you
unapply differences, for example, and I would be shocked if emacs didn't
have some tool for this.

-Peff

^ permalink raw reply

* Re: [msysGit] Re: Fourth incarnation of the msysGit herald
From: Johannes Schindelin @ 2007-10-26 16:47 UTC (permalink / raw)
  To: Pascal Obry; +Cc: Jan Hudec, Steffen Prohaska, msysgit, git
In-Reply-To: <47220B54.60508@obry.net>

Hi,

On Fri, 26 Oct 2007, Pascal Obry wrote:

> Jan Hudec a ?crit :
> > I got (or rather made me) a little time to try out it at work this week and
> > it looks really nice. Good job!
> 
> I tried it some time ago but there was no external module support. For
> example git-svn is missing and looking at the missing feature on the Web
> page I won't use it at the moment...

git-svn has nothing to do with external module support.

And remember, msysGit will fly sooner if people like _you_ help.

Ciao,
Dscho

^ permalink raw reply

* Re: git-svnimport
From: Johannes Schindelin @ 2007-10-26 16:45 UTC (permalink / raw)
  To: Gerrit Pape; +Cc: git
In-Reply-To: <20071026145520.2141.qmail@7a6a65599aed61.315fe32.mid.smarden.org>

Hi,

On Fri, 26 Oct 2007, Gerrit Pape wrote:

>  http://bugs.debian.org/436930
>  http://bugs.debian.org/447325
>  http://bugs.debian.org/447965
> 
> I'm not sure these are worth fixing, I'd rather suggest to drop 
> git-svnimport in favor of git-svn, and not installing it in future 
> versions.

I already proposed this.  The outcome was... silent.

Ciao,
Dscho

^ permalink raw reply

* Re: How to remove a specific hunk
From: Jeff King @ 2007-10-26 16:42 UTC (permalink / raw)
  To: Andreas Ericsson; +Cc: Pascal Obry, git
In-Reply-To: <472207AA.8030100@op5.se>

On Fri, Oct 26, 2007 at 05:28:42PM +0200, Andreas Ericsson wrote:

> Once you've added the other two hunks, they'll no longer show up in
> git-diff, so you can do something like this:
>
> $ git-add -i; # add the other two hunks to commit
> $ git-diff > middle-hunk.patch
> $ git-apply -R middle-hunk.patch
> test, test, test
> $ git-apply middle-hunk.patch

That has the side effect of changing the index state. My preferred
method would be to just excise the hunk from the diff using an editor
(and this actually has nothing to do with git; you could be using
diff/patch):

git-diff file >patch
# remove every hunk except what you want to cut out
$EDITOR patch
git-apply -R patch

-Peff

^ permalink raw reply

* Re: [PATCH] Fix regression in fast-import.c due to strbufs.
From: cpettitt @ 2007-10-26 16:39 UTC (permalink / raw)
  To: Pierre Habouzit, Shawn O. Pearce, cpettitt, Git Mailing List
In-Reply-To: <20071026075912.GA25365@artemis.corp>

On 10/26/07, Pierre Habouzit <madcoder@debian.org> wrote:
> Without this strbuf_release, it yields a double free later, the command is
> in fact stashed, and this is not a memory leak.
>
> Signed-off-by: Pierre Habouzit <madcoder@debian.org>

Pierre,

You nailed it! No more double frees and no segfault.

Thanks,
Chris

^ permalink raw reply

* Re: Trouble with case-insensitive filesystems
From: Jeff King @ 2007-10-26 16:34 UTC (permalink / raw)
  To: Andreas Ericsson; +Cc: git
In-Reply-To: <4722064C.1000201@op5.se>

On Fri, Oct 26, 2007 at 05:22:52PM +0200, Andreas Ericsson wrote:

> There are no areas in git to patch. There's no sane way to handle your
> case, so the best you could opt for is to import it to a system with
> sane case-handling, alter the repo so no two filenames clash, and then
> check it out on your case-insensitive filesystem. Note that you'll

You don't need a sane system, since git's index provides one:

  # make our new repo without checking anything out
  git-clone -n /path/to/other/repo repo
  cd repo

  # grab a text representation of what would be checked out
  git-ls-tree -r HEAD >files
  # fix up any broken filenames
  $EDITOR files
  # and shove it into the index
  git-update-index --index-info <files

  # update your working tree
  git-checkout-index -a
  # and optionally save the commit
  git-commit -m 'broken filenames hack'

Of course, all of the prior commits won't be usable. You would have to
repeat this hack on every commit using git-filter-branch for that.

-Peff

^ permalink raw reply

* Re: Trouble with case-insensitive filesystems
From: Rocco Rutte @ 2007-10-26 16:11 UTC (permalink / raw)
  To: git
In-Reply-To: <4722064C.1000201@op5.se>

Hi,

* Andreas Ericsson [07-10-26 17:22:52 +0200] wrote:

> There are no areas in git to patch. There's no sane way to handle your
> case, so the best you could opt for is to import it to a system with
> sane case-handling, alter the repo so no two filenames clash, and then
> check it out on your case-insensitive filesystem. Note that you'll
> have to make sure that you never check anything out prior to the
> commit that renames the case-name clashes, or you'll end up with this
> same trouble all over again.

Personally I don't have a problem with that (since I do no work with 
that repo). But IMHO it's bad to leave people without a clue what could 
be wrong when git-status right after git-checkout/git-clone reports 
changes. Btw, mercurial reports the problem so immediately know what's 
wrong.

> On a side note; Please don't set the Reply-To: header for mails to
> git@vger.kernel.org. Some consider it rude, and it makes the ones
> you're asking for help have to work if they want to provide you
> with anything off-list. It's a tad rude.

I didn't set a Reply-To: header, just Mail-Followup-To: which is exactly 
what I want (in mutt language): Private mails via reply only go to me 
and list-reply goes to the list.

Rocco

^ permalink raw reply

* Re: [PATCH] git-mailsplit: with maildirs try to process new/ if cur/ is empty
From: Fernando J. Pereda @ 2007-10-26 16:01 UTC (permalink / raw)
  To: Gerrit Pape; +Cc: git, Junio C Hamano
In-Reply-To: <20071026141539.29928.qmail@d3691352d65cf2.315fe32.mid.smarden.org>

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

On Fri, Oct 26, 2007 at 02:15:39PM +0000, Gerrit Pape wrote:
> When saving patches to a maildir with e.g. mutt, the files are put into
> the new/ subdirectory of the maildir, not cur/.  This makes git-am state
> "Nothing to do.".  This patch lets git-mailsplit fallback to new/ if the
> cur/ subdirectory is empty.
> 
> This was reported by Joey Hess through
>  http://bugs.debian.org/447396
> 

By that reasoning, you should make it parse both cur/ and new/.

This didn't bit me because I always check mails I queue, so they ended
up in cur/.

Other than that, ack from me.

- ferdy

-- 
Fernando J. Pereda Garcimartín
20BB BDC3 761A 4781 E6ED  ED0B 0A48 5B0C 60BD 28D4

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

^ permalink raw reply

* Re: [msysGit] Re: Fourth incarnation of the msysGit herald
From: Pascal Obry @ 2007-10-26 15:44 UTC (permalink / raw)
  To: Jan Hudec; +Cc: Steffen Prohaska, Johannes Schindelin, msysgit, git
In-Reply-To: <20071026152143.GB4819@efreet.light.src>

Jan Hudec a écrit :
> I got (or rather made me) a little time to try out it at work this week and
> it looks really nice. Good job!

I tried it some time ago but there was no external module support. For
example git-svn is missing and looking at the missing feature on the Web
page I won't use it at the moment...

The Cygwin/Git module is works very well. And Cygwin is definitely a
nice toolset (X, bash, ssh, rsync, ...) when you are forced working on
Windows :)

Pascal.

-- 

--|------------------------------------------------------
--| Pascal Obry                           Team-Ada Member
--| 45, rue Gabriel Peri - 78114 Magny Les Hameaux FRANCE
--|------------------------------------------------------
--|              http://www.obry.net
--| "The best way to travel is by means of imagination"
--|
--| gpg --keyserver wwwkeys.pgp.net --recv-key C1082595

^ permalink raw reply

* Re: How to remove a specific hunk
From: Pascal Obry @ 2007-10-26 15:38 UTC (permalink / raw)
  To: Andreas Ericsson; +Cc: git
In-Reply-To: <472207AA.8030100@op5.se>

Andreas,

> Once you've added the other two hunks, they'll no longer show up in
> git-diff, so you can do something like this:
> 
> $ git-add -i; # add the other two hunks to commit
> $ git-diff > middle-hunk.patch
> $ git-apply -R middle-hunk.patch
> test, test, test
> $ git-apply middle-hunk.patch

Thanks, this will clearly work. I was expecting something more
integrated like a "git reset --interactive" or something like that :)

Pascal.

-- 

--|------------------------------------------------------
--| Pascal Obry                           Team-Ada Member
--| 45, rue Gabriel Peri - 78114 Magny Les Hameaux FRANCE
--|------------------------------------------------------
--|              http://www.obry.net
--| "The best way to travel is by means of imagination"
--|
--| gpg --keyserver wwwkeys.pgp.net --recv-key C1082595

^ permalink raw reply

* Re: Trouble with case-insensitive filesystems
From: Jean-François Veillette @ 2007-10-26 15:29 UTC (permalink / raw)
  To: Rocco Rutte; +Cc: git
In-Reply-To: <20071026145204.GA294@localhost.daprodeges.fqdn.th-h.de>

You can workaround this by creating a disk-image with a case- 
sensitive filesystem, then do your work inside that virtual drive.

- jfv

Le 07-10-26 à 10:52, Rocco Rutte a écrit :

> Hi,
>
> after importing the opensolaris hg repo into git, I noticed that  
> git gets confused if the repo contains files that clash on case- 
> insensitive filesystems (here on OS X, I can't test Cygwin and  
> Win32). git-checkout tells me that these files are modified, git- 
> status gives me:
>
> $ git status
> # On branch master
> # Changed but not updated:
> #   (use "git add <file>..." to update what will be committed)
> #
> #       modified:   usr/src/cmd/lp/filter/postscript/font/devpost/HB
> #       modified:   usr/src/cmd/lp/filter/postscript/font/devpost/ 
> HB.name
> #       modified:   usr/src/cmd/lp/filter/postscript/font/devpost/HI
> #       modified:   usr/src/cmd/lp/filter/postscript/font/devpost/ 
> HI.name
> #       modified:   usr/src/cmd/lp/filter/postscript/font/devpost/HX
> #       modified:   usr/src/cmd/lp/filter/postscript/font/devpost/ 
> HX.name
> #       modified:   usr/src/cmd/lp/filter/postscript/font/devpost/ 
> charlib/LH
> #       modified:   usr/src/lib/libldap4/common/Version.c
> #
> no changes added to commit (use "git add" and/or "git commit -a")
>
> ...without touching anything. Yes, there's a version.c file next to  
> Version.c, HI.name next to Hi.name and so on.
>
> I'm not really sure what I'm expecting git to do, but I guess I  
> want it to abort a checkout and only continue with -f. But at the  
> very least, it should issue a big fat warning (one may decide to  
> work in some area without clashes).
>
> I really have no idea how to efficiently detect that at runtime and  
> which areas of git to look at for patching...
>
> Rocco
> -
> To unsubscribe from this list: send the line "unsubscribe git" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: How to remove a specific hunk
From: Andreas Ericsson @ 2007-10-26 15:28 UTC (permalink / raw)
  To: Pascal Obry; +Cc: git
In-Reply-To: <4722036E.5030204@wanadoo.fr>

Pascal Obry wrote:
> Hello,
> 
> I'm very new to Git... but start to love it :)
> 
> Before committing sometimes I want to remove a specific hunk. Say in
> file a.txt I have in the diff 3 hunks, I want to revert/delete/remove
> the second one. Is there a way to do that ?
> 
> I understand that I can git add interactive and select the hook I want
> to commit, but this is not fully equivalent. I'm not yet ready to commit
> I just want to undo a specific change and test the code without it...
> 
> Any idea ?
> 

Once you've added the other two hunks, they'll no longer show up in
git-diff, so you can do something like this:

$ git-add -i; # add the other two hunks to commit
$ git-diff > middle-hunk.patch
$ git-apply -R middle-hunk.patch
test, test, test
$ git-apply middle-hunk.patch

Completely untested, so take a copy before you try it.

-- 
Andreas Ericsson                   andreas.ericsson@op5.se
OP5 AB                             www.op5.se
Tel: +46 8-230225                  Fax: +46 8-230231

^ 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