* Re: [PATCH 1/2] Add an (optional, since expensive) test for >2g clones
From: Junio C Hamano @ 2009-03-05 23:08 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: git, gitster, Johannes Sixt
In-Reply-To: <96a26f3a883890b3e56c867e8183618784837d4d.1236268730u.git.johannes.schindelin@gmx.de>
Johannes Schindelin <johannes.schindelin@gmx.de> writes:
> +test_expect_success 'setup' '
> +
> + git config pack.compression 0 &&
> + git config pack.depth 0 &&
> + blobsize=$((20*1024*1024))
> + blobcount=$((2*1024*1024*1024/$blobsize+1))
> + i=1
What happened to the && chain?
^ permalink raw reply
* Re: git push bash completion
From: Markus Heidelberg @ 2009-03-05 23:15 UTC (permalink / raw)
To: Sverre Rabbelier; +Cc: Git Mailing List
In-Reply-To: <fabb9a1e0903051430k5628fe26v4749360e026ac7d2@mail.gmail.com>
Sverre Rabbelier, 05.03.2009:
> Heya,
>
> Observe:
> $ git push ori<tab>
> git push origin
>
> $ git push -f ori<tab>
> git push -f origin/
>
> Something weird going on there, or is this intentional and am I
> missing something?
Something similar happens with fetch and pull. They only complete the
remote name, when exactly 2 words are existing on the command line
("git" and the subcommand) by: if [ "$COMP_CWORD" = 2 ]
Doesn't seem right.
Markus
^ permalink raw reply
* Re: [PATCH] git-p4: improve performance with large files
From: thestar @ 2009-03-06 0:01 UTC (permalink / raw)
To: Sam Hocevar; +Cc: git
In-Reply-To: <20090305172332.GF25693@zoy.org>
Quoting Sam Hocevar <sam@zoy.org>:
> On Thu, Mar 05, 2009, thestar@fussycoder.id.au wrote:
>
>> > The current git-p4 way of concatenating strings performs in O(n^2)
>> >and is therefore terribly slow with large files because of unnecessary
>> >memory copies. The following patch makes the operation O(n).
>>
>> The reason why it uses simple concatenation is to cut down on memory usage.
>> - It is a tradeoff.
>>
>> I think the modification you have made below is reasonable, however be
>> aware that memory usage could double, which substantially reduce the
>> size of the changesets that git-p4 would be able to import /at all/,
>> rather than to merely be slow.
>
> Uhm, no. The memory usage could be an additional X, where X is the
> size of the biggest file in the commit. Remember that commit() stores
> the complete commit data in memory before sending it to fast-import.
> Also, on my machine the extra memory is already used because at some
> point, "text += foo" calls realloc() anyway and often duplicates the
> memory used by text.
You are correct - sorry, got confused as I somehow got mistaken that
that form (foo += bar) can potentially be optimized, but it doesn't.
That's what I get for not paying enough attention to the language
used... :(
> The ideal solution is to use a generator and refactor the commit
> handling as a stream. I am working on that but it involves deeper
> changes, so as I am not sure it will be accepted, I'm providing the
> attached compromise patch first. At least it solves the appaling speed
> issue. I tuned it so that it never uses more than 32 MiB extra memory.
That is definitely the ideal solution - I would hope that it gets
accepted if you manage to cut down on memory usage - as it really is a
limitation for certain repositories. (Infact, this is why I no-longer
use git-p4).
>
> Signed-off-by: Sam Hocevar <sam@zoy.org>
> ---
> contrib/fast-import/git-p4 | 10 +++++++++-
> 1 files changed, 9 insertions(+), 1 deletions(-)
>
> diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4
> index 3832f60..151ae1c 100755
> --- a/contrib/fast-import/git-p4
> +++ b/contrib/fast-import/git-p4
> @@ -984,11 +984,19 @@ class P4Sync(Command):
> while j < len(filedata):
> stat = filedata[j]
> j += 1
> + data = []
> text = ''
> while j < len(filedata) and filedata[j]['code'] in
> ('text', 'unicod
> e', 'binary'):
> - text += filedata[j]['data']
> + data.append(filedata[j]['data'])
> del filedata[j]['data']
> + # p4 sends 4k chunks, make sure we don't use more
> than 32 MiB
> + # of additional memory while rebuilding the file data.
> + if len(data) > 8192:
> + text += ''.join(data)
> + data = []
> j += 1
> + text += ''.join(data)
> + del data
>
> if not stat.has_key('depotFile'):
> sys.stderr.write("p4 print fails with: %s\n" % repr(stat))
>
> --
> Sam.
> --
> 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: [Orinoco-users] linux-firmware binary corruption with gitweb
From: Jakub Narebski @ 2009-03-06 0:03 UTC (permalink / raw)
To: Dave; +Cc: Pavel Roskin, git, linux-kernel, orinoco-users, dwmw2, J.H.
In-Reply-To: <49AF1429.9080009@gmail.com>
On Thu, 5 March 2009, Dave wrote:
> Jakub Narebski wrote:
>> Dave <kilroyd@googlemail.com> writes:
>>>> My strong impression is that the recoding takes place on the server. I
>>>> think the bug should be reported to the gitweb maintainers unless it a
>>>> local breakage on the kernel.org site.
It is on server, but kernel.org runs modified version of gitweb, and
the bug is in the modifications. See below.
CC-ed John 'Warthog9' Hawley, maintainer of gitweb on kernel.org
>>>>
>>> Thanks Pavel.
>>>
>>> I just did a quick scan of the gitweb README - is this an issue with the
>>> $mimetypes_file or $fallback_encoding configurations variables?
>>
>> First, what version of gitweb do you use? It should be in 'Generator'
>> meta header, or (in older gitweb) in comments in HTML source at the
>> top of the page.
>
> Not sure where I'd find the meta header,
<meta name="generator" content="gitweb/1.4.5-rc0.GIT-dirty git/1.6.1.1"/>
> but at the top of the HTML:
>
> <!-- git web interface version 1.4.5-rc0.GIT-dirty, (C) 2005-2006, Kay
> Sievers <kay.sievers@vrfy.org>, Christian Gierke -->
> <!-- git core binaries version 1.6.1.1 -->
The question was if it is extremely old version of gitweb, without fix
of raw blob ('blob_plain') output for non-utf8, non-text files. But the
answer is that it is _modified_ version of gitweb, see below.
>
>> Second, the file is actually sent to browser 'as is', using binmode :raw
>> (or at least should be according to my understanding of Perl). And *.bin
>> binary file gets application/octet-stream mimetype, and doesn't send any
>> charset info. git.kernel.org should have modern enough gitweb to use this.
>> Strange...
>
> Dug around gitweb.perl in the main git repo. Then looked at the
> git/warthog9/gitweb.git repo (after noting the Git Wiki says kernel.org
> is running John Hawley's branch).
>
> One notable change to git_blob_plain:
>
> undef $/;
> binmode STDOUT, ':raw';
> - print <$fd>;
> + #print <$fd>;
> + $output .= <$fd>;
> binmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi
> $/ = "\n";
>
> close $fd;
> +
> + return $output;
>
> If that's the code that's running, doesn't that mean the output mode
> change doesn't impact the concatenation to $output? So the blob gets utf
> encoding when actually printed.
That is the culprit. kernel.org runs modified version of gitweb, with
added caching. I guess that the above change was to have 'blob_plain'
output cached... but it loses "rawness", and I guess it also loses
mimetype info (unless "print $cgi->header(...)" is also changed to
appending to $output).
One possible solution would be to redirect STDOUT to scalar, and return
that scalar; do that always when caching _output_, and print :raw all
cached _output_ data.
close STDOUT;
open STDOUT, '>', \$output or die "Can't open STDOUT: $!";
BTW. f5aa79d (gitweb: safely output binary files for 'blob_plain' action)
was my third patch for git...
--
Jakub Narebski
Poland
^ permalink raw reply
* Re: [RFC PATCH] git-svn does not support intermediate directories?
From: Michael Lai @ 2009-03-06 0:12 UTC (permalink / raw)
To: Tim Stoakes, git
In-Reply-To: <20090304043019.GC20790@mail.rocksoft.com>
I did some additional hacking and may have found a slightly cleaner
way of at least fixing the problems with "git svn fetch". The problem
with the wrong paths being initialized for branches and tags is fairly
minor (since you can just edit the config by hand), so I'll probably
address that later, if I have time. Here's the patch (I hope I'm
doing this right):
diff --git a/git-svn.perl b/git-svn.perl
index 959eb52..174f266 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -2351,7 +2351,11 @@ sub match_paths {
if (my $path = $paths->{"/$self->{path}"}) {
return ($path->{action} eq 'D') ? 0 : 1;
}
- $self->{path_regex} ||= qr/^\/\Q$self->{path}\E\//;
+ my $repos_root = $self->ra->{repos_root};
+ my $intermediate_path = $self->{url};
+ $intermediate_path =~ s#^\Q$repos_root\E(/|$)#\/#;
+ $intermediate_path .= '/' if $intermediate_path;
+ $self->{path_regex} ||= qr/^\/\Q$intermediate_path$self->{path}\E\//;
if (grep /$self->{path_regex}/, keys %$paths) {
return 1;
}
--
On Tue, Mar 3, 2009 at 8:30 PM, Tim Stoakes <tim@stoakes.net> wrote:
> Michael Lai(myllai@gmail.com)@030309-17:43:
>> After spending some hours struggling with git svn, it would appear
>> that it does not support svn projects stored in paths similar to
>> "http://foo.com/svn/repos/bar/myproject", where "myproject" uses the
>> standard SVN tags/trunk/branches layout. I'm currently using git
>> 1.6.1, though I tried this with 1.6.2-rc2 as well. The resulting
>> .git/config looks something like this:
>>
>> [svn-remote "svn"]
>> url = http://foo.com/svn/repos/bar
>> fetch = myproject/trunk:refs/remotes/trunk
>> branches = bar/myproject/branches/*:refs/remotes/*
>> tags = bar/myproject/tags/*:refs/remotes/tags/*
>>
>> Yes, that's a redundant "bar" directory under "branches =" and "tags
>> =". The issue seems to lie in git-svn doing something intelligent to
>> extract the appropriate trunk directory. For the branches and tags,
>> however, it just takes the full URL and removes the repository root
>> (http://foo.com/svn/repos/bar) to produce "bar/myproject/{branches,
>> tags}/*". The second effect is that "git svn fetch" will run but exit
>> quietly without actually pulling anything from the repository. I
>> tracked down an existing thread on the mailing list from a while ago
>> (Feb 4th, title of "git-svn doesn't fetch anything"), but there was no
>> resolution.
>
> I've just run into this exact same issue.
>
>> There is a quick workaround, which was to make this change to match_paths:
>> < $self->{path_regex} ||= qr/^\/\Q$self->{path}\E\//;
>> ---
>> > $self->{path_regex} ||= qr/\/\Q$self->{path}\E\//;
>>
>> The additional "bar" directory gets pulled in when git-svn tries to
>> determine what paths to pull down, and tries to match
>> "/myproject/trunk" to "/bar/myproject/trunk". I've merely put a
>> band-aid on the situation. My perl is rudimentary at best, or I'd
>> have spent additional time to try to put in a "proper" patch, but was
>> wondering if anyone else had run into this problem and would be
>> willing to put in a fix (or point me in the right direction, that
>> works too).
>
> I messed about with disabling $Git::SVN::_minimize_url, but that seemed
> to break other things.
>
> Made worse for me was the fact that my 'bar' in the present was renamed
> from 'baz' in the past, so git-svn couldn't find it at r1. Very
> confusing!
>
> I'd like a nicer solution too.
>
> Tim
>
> --
> Tim Stoakes
>
^ permalink raw reply related
* [JGIT PATCH 1/2] Fix merges involving subtrees with StrategySimpleTwoWayInCore
From: Shawn O. Pearce @ 2009-03-06 1:05 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git
JGit mismerged two trees when the common ancestor tree contained two
trees named libelf-po and libelf, and libelf was modified on one side
of the merge, while libelf-po was unmodified by both:
040000 tree ... libelf-po
040000 tree ... libelf
Above is the correct sort order, as the second tree, libelf, must be
sorted as though it ends with '/', and thus comes after libelf-po.
JGit flipped the order during a merge as the strategy added the modified
subtree "libelf" directly to the DirCacheBuilder, rather than unfolding
its contents into the DirCache. The result was a tree entry where only
file type entries were expected, and the DirCacheBuilder resorted its
contents to place "libelf" before "libelf-po".
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
This patch fixes the data corruption I reported earlier today,
and that Peff helped me find so quickly.
The second patch in this series fixes a long standing set of
bugs with the merge code when dealing with concurrent (but not
conflicting) modifications in subtrees.
.../org/spearce/jgit/merge/SimpleMergeTest.java | 93 ++++++++++++++++++++
.../org/spearce/jgit/dircache/DirCacheBuilder.java | 17 +++--
.../jgit/merge/StrategySimpleTwoWayInCore.java | 27 +++---
3 files changed, 119 insertions(+), 18 deletions(-)
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/merge/SimpleMergeTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/merge/SimpleMergeTest.java
index 96064f5..e99f017 100644
--- a/org.spearce.jgit.test/tst/org/spearce/jgit/merge/SimpleMergeTest.java
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/merge/SimpleMergeTest.java
@@ -36,10 +36,20 @@
*/
package org.spearce.jgit.merge;
+import java.io.ByteArrayInputStream;
import java.io.IOException;
+import org.spearce.jgit.dircache.DirCache;
+import org.spearce.jgit.dircache.DirCacheBuilder;
+import org.spearce.jgit.dircache.DirCacheEntry;
+import org.spearce.jgit.lib.Commit;
+import org.spearce.jgit.lib.Constants;
+import org.spearce.jgit.lib.FileMode;
import org.spearce.jgit.lib.ObjectId;
+import org.spearce.jgit.lib.ObjectWriter;
+import org.spearce.jgit.lib.PersonIdent;
import org.spearce.jgit.lib.RepositoryTestCase;
+import org.spearce.jgit.treewalk.TreeWalk;
public class SimpleMergeTest extends RepositoryTestCase {
@@ -83,4 +93,87 @@ public void testTrivialTwoWay_conflict() throws IOException {
boolean merge = ourMerger.merge(new ObjectId[] { db.resolve("f"), db.resolve("g") });
assertFalse(merge);
}
+
+ public void testTrivialTwoWay_validSubtreeSort() throws Exception {
+ final DirCache treeB = DirCache.read(db);
+ final DirCache treeO = DirCache.read(db);
+ final DirCache treeT = DirCache.read(db);
+ {
+ final DirCacheBuilder b = treeB.builder();
+ final DirCacheBuilder o = treeO.builder();
+ final DirCacheBuilder t = treeT.builder();
+
+ b.add(makeEntry("libelf-po/a", FileMode.REGULAR_FILE));
+ b.add(makeEntry("libelf/c", FileMode.REGULAR_FILE));
+
+ o.add(makeEntry("Makefile", FileMode.REGULAR_FILE));
+ o.add(makeEntry("libelf-po/a", FileMode.REGULAR_FILE));
+ o.add(makeEntry("libelf/c", FileMode.REGULAR_FILE));
+
+ t.add(makeEntry("libelf-po/a", FileMode.REGULAR_FILE));
+ t.add(makeEntry("libelf/c", FileMode.REGULAR_FILE, "blah"));
+
+ b.finish();
+ o.finish();
+ t.finish();
+ }
+
+ final ObjectWriter ow = new ObjectWriter(db);
+ final ObjectId b = commit(ow, treeB, new ObjectId[] {});
+ final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
+ final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
+
+ Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
+ boolean merge = ourMerger.merge(new ObjectId[] { o, t });
+ assertTrue(merge);
+
+ final TreeWalk tw = new TreeWalk(db);
+ tw.setRecursive(true);
+ tw.reset(ourMerger.getResultTreeId());
+
+ assertTrue(tw.next());
+ assertEquals("Makefile", tw.getPathString());
+ assertCorrectId(treeO, tw);
+
+ assertTrue(tw.next());
+ assertEquals("libelf-po/a", tw.getPathString());
+ assertCorrectId(treeO, tw);
+
+ assertTrue(tw.next());
+ assertEquals("libelf/c", tw.getPathString());
+ assertCorrectId(treeT, tw);
+
+ assertFalse(tw.next());
+ }
+
+ private void assertCorrectId(final DirCache treeT, final TreeWalk tw) {
+ assertEquals(treeT.getEntry(tw.getPathString()).getObjectId(), tw
+ .getObjectId(0));
+ }
+
+ private ObjectId commit(final ObjectWriter ow, final DirCache treeB,
+ final ObjectId[] parentIds) throws Exception {
+ final Commit c = new Commit(db);
+ c.setTreeId(treeB.writeTree(ow));
+ c.setAuthor(new PersonIdent("A U Thor", "a.u.thor", 1L, 0));
+ c.setCommitter(c.getAuthor());
+ c.setParentIds(parentIds);
+ c.setMessage("Tree " + c.getTreeId().name());
+ return ow.writeCommit(c);
+ }
+
+ private DirCacheEntry makeEntry(final String path, final FileMode mode)
+ throws Exception {
+ return makeEntry(path, mode, path);
+ }
+
+ private DirCacheEntry makeEntry(final String path, final FileMode mode,
+ final String content) throws Exception {
+ final DirCacheEntry ent = new DirCacheEntry(path);
+ ent.setFileMode(mode);
+ final byte[] contentBytes = Constants.encode(content);
+ ent.setObjectId(new ObjectWriter(db).computeBlobSha1(
+ contentBytes.length, new ByteArrayInputStream(contentBytes)));
+ return ent;
+ }
}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheBuilder.java b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheBuilder.java
index 9a9d174..aee12fb 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheBuilder.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheBuilder.java
@@ -41,6 +41,7 @@
import java.util.Arrays;
import org.spearce.jgit.lib.AnyObjectId;
+import org.spearce.jgit.lib.FileMode;
import org.spearce.jgit.lib.Repository;
import org.spearce.jgit.lib.WindowCursor;
import org.spearce.jgit.treewalk.AbstractTreeIterator;
@@ -133,6 +134,8 @@ public void keep(final int pos, int cnt) {
* UTF-8 encoded prefix to mount the tree's entries at. If the
* path does not end with '/' one will be automatically inserted
* as necessary.
+ * @param stage
+ * stage of the entries when adding them.
* @param db
* repository the tree(s) will be read from during recursive
* traversal. This must be the same repository that the resulting
@@ -146,8 +149,8 @@ public void keep(final int pos, int cnt) {
* @throws IOException
* a tree cannot be read to iterate through its entries.
*/
- public void addTree(final byte[] pathPrefix, final Repository db,
- final AnyObjectId tree) throws IOException {
+ public void addTree(final byte[] pathPrefix, final int stage,
+ final Repository db, final AnyObjectId tree) throws IOException {
final TreeWalk tw = new TreeWalk(db);
tw.reset();
final WindowCursor curs = new WindowCursor();
@@ -159,16 +162,16 @@ public void addTree(final byte[] pathPrefix, final Repository db,
}
tw.setRecursive(true);
if (tw.next()) {
- final DirCacheEntry newEntry = toEntry(tw);
+ final DirCacheEntry newEntry = toEntry(stage, tw);
beforeAdd(newEntry);
fastAdd(newEntry);
while (tw.next())
- fastAdd(toEntry(tw));
+ fastAdd(toEntry(stage, tw));
}
}
- private DirCacheEntry toEntry(final TreeWalk tw) {
- final DirCacheEntry e = new DirCacheEntry(tw.getRawPath());
+ private DirCacheEntry toEntry(final int stage, final TreeWalk tw) {
+ final DirCacheEntry e = new DirCacheEntry(tw.getRawPath(), stage);
final AbstractTreeIterator i;
i = tw.getTree(0, AbstractTreeIterator.class);
@@ -184,6 +187,8 @@ public void finish() {
}
private void beforeAdd(final DirCacheEntry newEntry) {
+ if (FileMode.TREE.equals(newEntry.getRawMode()))
+ throw bad(newEntry, "Adding subtree not allowed");
if (sorted && entryCnt > 0) {
final DirCacheEntry lastEntry = entries[entryCnt - 1];
final int cr = DirCache.cmp(lastEntry, newEntry);
diff --git a/org.spearce.jgit/src/org/spearce/jgit/merge/StrategySimpleTwoWayInCore.java b/org.spearce.jgit/src/org/spearce/jgit/merge/StrategySimpleTwoWayInCore.java
index 893add9..0f8b4e1 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/merge/StrategySimpleTwoWayInCore.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/merge/StrategySimpleTwoWayInCore.java
@@ -43,6 +43,7 @@
import org.spearce.jgit.dircache.DirCacheBuilder;
import org.spearce.jgit.dircache.DirCacheEntry;
import org.spearce.jgit.errors.UnmergedPathException;
+import org.spearce.jgit.lib.FileMode;
import org.spearce.jgit.lib.ObjectId;
import org.spearce.jgit.lib.Repository;
import org.spearce.jgit.treewalk.AbstractTreeIterator;
@@ -143,27 +144,29 @@ else if (modeB == modeT && tw.idEqual(T_BASE, T_THEIRS))
}
private void same() throws IOException {
- if (tw.isSubtree())
- builder.addTree(tw.getRawPath(), db, tw.getObjectId(1));
- else
- add(T_OURS, DirCacheEntry.STAGE_0);
+ add(T_OURS, DirCacheEntry.STAGE_0);
}
- private void conflict() {
+ private void conflict() throws IOException {
add(T_BASE, DirCacheEntry.STAGE_1);
add(T_OURS, DirCacheEntry.STAGE_2);
add(T_THEIRS, DirCacheEntry.STAGE_3);
}
- private void add(final int tree, final int stage) {
+ private void add(final int tree, final int stage) throws IOException {
final AbstractTreeIterator i = getTree(tree);
if (i != null) {
- final DirCacheEntry e;
-
- e = new DirCacheEntry(tw.getRawPath(), stage);
- e.setObjectIdFromRaw(i.idBuffer(), i.idOffset());
- e.setFileMode(tw.getFileMode(tree));
- builder.add(e);
+ if (FileMode.TREE.equals(tw.getRawMode(tree))) {
+ builder.addTree(tw.getRawPath(), stage, db, tw
+ .getObjectId(tree));
+ } else {
+ final DirCacheEntry e;
+
+ e = new DirCacheEntry(tw.getRawPath(), stage);
+ e.setObjectIdFromRaw(i.idBuffer(), i.idOffset());
+ e.setFileMode(tw.getFileMode(tree));
+ builder.add(e);
+ }
}
}
--
1.6.2.96.gc65e7
^ permalink raw reply related
* [JGIT PATCH 2/2] Fix merges involving clean subtrees with StrategySimpleTwoWayInCore
From: Shawn O. Pearce @ 2009-03-06 1:05 UTC (permalink / raw)
To: Robin Rosenberg; +Cc: git
In-Reply-To: <1236301524-18540-1-git-send-email-spearce@spearce.org>
If a both sides modify files in the same subtree, but do so in a
non-conflicting way, we should still be able to merge them by an
automated merge strategy. Recursing into the subtree permits us
to do this merge.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
.../org/spearce/jgit/merge/SimpleMergeTest.java | 204 ++++++++++++++++++++
.../jgit/merge/StrategySimpleTwoWayInCore.java | 32 ++-
2 files changed, 225 insertions(+), 11 deletions(-)
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/merge/SimpleMergeTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/merge/SimpleMergeTest.java
index e99f017..c5755a8 100644
--- a/org.spearce.jgit.test/tst/org/spearce/jgit/merge/SimpleMergeTest.java
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/merge/SimpleMergeTest.java
@@ -146,6 +146,210 @@ public void testTrivialTwoWay_validSubtreeSort() throws Exception {
assertFalse(tw.next());
}
+ public void testTrivialTwoWay_concurrentSubtreeChange() throws Exception {
+ final DirCache treeB = DirCache.read(db);
+ final DirCache treeO = DirCache.read(db);
+ final DirCache treeT = DirCache.read(db);
+ {
+ final DirCacheBuilder b = treeB.builder();
+ final DirCacheBuilder o = treeO.builder();
+ final DirCacheBuilder t = treeT.builder();
+
+ b.add(makeEntry("d/o", FileMode.REGULAR_FILE));
+ b.add(makeEntry("d/t", FileMode.REGULAR_FILE));
+
+ o.add(makeEntry("d/o", FileMode.REGULAR_FILE, "o !"));
+ o.add(makeEntry("d/t", FileMode.REGULAR_FILE));
+
+ t.add(makeEntry("d/o", FileMode.REGULAR_FILE));
+ t.add(makeEntry("d/t", FileMode.REGULAR_FILE, "t !"));
+
+ b.finish();
+ o.finish();
+ t.finish();
+ }
+
+ final ObjectWriter ow = new ObjectWriter(db);
+ final ObjectId b = commit(ow, treeB, new ObjectId[] {});
+ final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
+ final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
+
+ Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
+ boolean merge = ourMerger.merge(new ObjectId[] { o, t });
+ assertTrue(merge);
+
+ final TreeWalk tw = new TreeWalk(db);
+ tw.setRecursive(true);
+ tw.reset(ourMerger.getResultTreeId());
+
+ assertTrue(tw.next());
+ assertEquals("d/o", tw.getPathString());
+ assertCorrectId(treeO, tw);
+
+ assertTrue(tw.next());
+ assertEquals("d/t", tw.getPathString());
+ assertCorrectId(treeT, tw);
+
+ assertFalse(tw.next());
+ }
+
+ public void testTrivialTwoWay_conflictSubtreeChange() throws Exception {
+ final DirCache treeB = DirCache.read(db);
+ final DirCache treeO = DirCache.read(db);
+ final DirCache treeT = DirCache.read(db);
+ {
+ final DirCacheBuilder b = treeB.builder();
+ final DirCacheBuilder o = treeO.builder();
+ final DirCacheBuilder t = treeT.builder();
+
+ b.add(makeEntry("d/o", FileMode.REGULAR_FILE));
+ b.add(makeEntry("d/t", FileMode.REGULAR_FILE));
+
+ o.add(makeEntry("d/o", FileMode.REGULAR_FILE));
+ o.add(makeEntry("d/t", FileMode.REGULAR_FILE, "o !"));
+
+ t.add(makeEntry("d/o", FileMode.REGULAR_FILE, "t !"));
+ t.add(makeEntry("d/t", FileMode.REGULAR_FILE, "t !"));
+
+ b.finish();
+ o.finish();
+ t.finish();
+ }
+
+ final ObjectWriter ow = new ObjectWriter(db);
+ final ObjectId b = commit(ow, treeB, new ObjectId[] {});
+ final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
+ final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
+
+ Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
+ boolean merge = ourMerger.merge(new ObjectId[] { o, t });
+ assertFalse(merge);
+ }
+
+ public void testTrivialTwoWay_leftDFconflict1() throws Exception {
+ final DirCache treeB = DirCache.read(db);
+ final DirCache treeO = DirCache.read(db);
+ final DirCache treeT = DirCache.read(db);
+ {
+ final DirCacheBuilder b = treeB.builder();
+ final DirCacheBuilder o = treeO.builder();
+ final DirCacheBuilder t = treeT.builder();
+
+ b.add(makeEntry("d/o", FileMode.REGULAR_FILE));
+ b.add(makeEntry("d/t", FileMode.REGULAR_FILE));
+
+ o.add(makeEntry("d", FileMode.REGULAR_FILE));
+
+ t.add(makeEntry("d/o", FileMode.REGULAR_FILE));
+ t.add(makeEntry("d/t", FileMode.REGULAR_FILE, "t !"));
+
+ b.finish();
+ o.finish();
+ t.finish();
+ }
+
+ final ObjectWriter ow = new ObjectWriter(db);
+ final ObjectId b = commit(ow, treeB, new ObjectId[] {});
+ final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
+ final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
+
+ Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
+ boolean merge = ourMerger.merge(new ObjectId[] { o, t });
+ assertFalse(merge);
+ }
+
+ public void testTrivialTwoWay_rightDFconflict1() throws Exception {
+ final DirCache treeB = DirCache.read(db);
+ final DirCache treeO = DirCache.read(db);
+ final DirCache treeT = DirCache.read(db);
+ {
+ final DirCacheBuilder b = treeB.builder();
+ final DirCacheBuilder o = treeO.builder();
+ final DirCacheBuilder t = treeT.builder();
+
+ b.add(makeEntry("d/o", FileMode.REGULAR_FILE));
+ b.add(makeEntry("d/t", FileMode.REGULAR_FILE));
+
+ o.add(makeEntry("d/o", FileMode.REGULAR_FILE));
+ o.add(makeEntry("d/t", FileMode.REGULAR_FILE, "o !"));
+
+ t.add(makeEntry("d", FileMode.REGULAR_FILE));
+
+ b.finish();
+ o.finish();
+ t.finish();
+ }
+
+ final ObjectWriter ow = new ObjectWriter(db);
+ final ObjectId b = commit(ow, treeB, new ObjectId[] {});
+ final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
+ final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
+
+ Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
+ boolean merge = ourMerger.merge(new ObjectId[] { o, t });
+ assertFalse(merge);
+ }
+
+ public void testTrivialTwoWay_leftDFconflict2() throws Exception {
+ final DirCache treeB = DirCache.read(db);
+ final DirCache treeO = DirCache.read(db);
+ final DirCache treeT = DirCache.read(db);
+ {
+ final DirCacheBuilder b = treeB.builder();
+ final DirCacheBuilder o = treeO.builder();
+ final DirCacheBuilder t = treeT.builder();
+
+ b.add(makeEntry("d", FileMode.REGULAR_FILE));
+
+ o.add(makeEntry("d", FileMode.REGULAR_FILE, "o !"));
+
+ t.add(makeEntry("d/o", FileMode.REGULAR_FILE));
+
+ b.finish();
+ o.finish();
+ t.finish();
+ }
+
+ final ObjectWriter ow = new ObjectWriter(db);
+ final ObjectId b = commit(ow, treeB, new ObjectId[] {});
+ final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
+ final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
+
+ Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
+ boolean merge = ourMerger.merge(new ObjectId[] { o, t });
+ assertFalse(merge);
+ }
+
+ public void testTrivialTwoWay_rightDFconflict2() throws Exception {
+ final DirCache treeB = DirCache.read(db);
+ final DirCache treeO = DirCache.read(db);
+ final DirCache treeT = DirCache.read(db);
+ {
+ final DirCacheBuilder b = treeB.builder();
+ final DirCacheBuilder o = treeO.builder();
+ final DirCacheBuilder t = treeT.builder();
+
+ b.add(makeEntry("d", FileMode.REGULAR_FILE));
+
+ o.add(makeEntry("d/o", FileMode.REGULAR_FILE));
+
+ t.add(makeEntry("d", FileMode.REGULAR_FILE, "t !"));
+
+ b.finish();
+ o.finish();
+ t.finish();
+ }
+
+ final ObjectWriter ow = new ObjectWriter(db);
+ final ObjectId b = commit(ow, treeB, new ObjectId[] {});
+ final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
+ final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
+
+ Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
+ boolean merge = ourMerger.merge(new ObjectId[] { o, t });
+ assertFalse(merge);
+ }
+
private void assertCorrectId(final DirCache treeT, final TreeWalk tw) {
assertEquals(treeT.getEntry(tw.getPathString()).getObjectId(), tw
.getObjectId(0));
diff --git a/org.spearce.jgit/src/org/spearce/jgit/merge/StrategySimpleTwoWayInCore.java b/org.spearce.jgit/src/org/spearce/jgit/merge/StrategySimpleTwoWayInCore.java
index 0f8b4e1..3ebe397 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/merge/StrategySimpleTwoWayInCore.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/merge/StrategySimpleTwoWayInCore.java
@@ -115,7 +115,7 @@ protected boolean mergeImpl() throws IOException {
final int modeO = tw.getRawMode(T_OURS);
final int modeT = tw.getRawMode(T_THEIRS);
if (modeO == modeT && tw.idEqual(T_OURS, T_THEIRS)) {
- same();
+ add(T_OURS, DirCacheEntry.STAGE_0);
continue;
}
@@ -124,8 +124,24 @@ protected boolean mergeImpl() throws IOException {
add(T_THEIRS, DirCacheEntry.STAGE_0);
else if (modeB == modeT && tw.idEqual(T_BASE, T_THEIRS))
add(T_OURS, DirCacheEntry.STAGE_0);
- else {
- conflict();
+ else if (tw.isSubtree()) {
+ if (nonTree(modeB)) {
+ add(T_BASE, DirCacheEntry.STAGE_1);
+ hasConflict = true;
+ }
+ if (nonTree(modeO)) {
+ add(T_OURS, DirCacheEntry.STAGE_2);
+ hasConflict = true;
+ }
+ if (nonTree(modeT)) {
+ add(T_THEIRS, DirCacheEntry.STAGE_3);
+ hasConflict = true;
+ }
+ tw.enterSubtree();
+ } else {
+ add(T_BASE, DirCacheEntry.STAGE_1);
+ add(T_OURS, DirCacheEntry.STAGE_2);
+ add(T_THEIRS, DirCacheEntry.STAGE_3);
hasConflict = true;
}
}
@@ -143,14 +159,8 @@ else if (modeB == modeT && tw.idEqual(T_BASE, T_THEIRS))
}
}
- private void same() throws IOException {
- add(T_OURS, DirCacheEntry.STAGE_0);
- }
-
- private void conflict() throws IOException {
- add(T_BASE, DirCacheEntry.STAGE_1);
- add(T_OURS, DirCacheEntry.STAGE_2);
- add(T_THEIRS, DirCacheEntry.STAGE_3);
+ private static boolean nonTree(final int mode) {
+ return mode != 0 && !FileMode.TREE.equals(mode);
}
private void add(final int tree, final int stage) throws IOException {
--
1.6.2.96.gc65e7
^ permalink raw reply related
* Re: [PATCH] git-p4: improve performance with large files
From: Junio C Hamano @ 2009-03-06 1:14 UTC (permalink / raw)
To: Simon Hausmann, Han-Wen Nienhuys; +Cc: git, Sam Hocevar
In-Reply-To: <20090305172332.GF25693@zoy.org>
Sam Hocevar <sam@zoy.org> writes:
> ... The ideal solution is to use a generator and refactor the commit
> handling as a stream. I am working on that but it involves deeper
> changes, so as I am not sure it will be accepted, I'm providing the
> attached compromise patch first. At least it solves the appaling speed
> issue. I tuned it so that it never uses more than 32 MiB extra memory.
>
> Signed-off-by: Sam Hocevar <sam@zoy.org>
> ---
I do not do p4, but the patch looks obviously correct. Comments?
> contrib/fast-import/git-p4 | 10 +++++++++-
> 1 files changed, 9 insertions(+), 1 deletions(-)
>
> diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4
> index 3832f60..151ae1c 100755
> --- a/contrib/fast-import/git-p4
> +++ b/contrib/fast-import/git-p4
> @@ -984,11 +984,19 @@ class P4Sync(Command):
> while j < len(filedata):
> stat = filedata[j]
> j += 1
> + data = []
> text = ''
> while j < len(filedata) and filedata[j]['code'] in ('text', 'unicod
> e', 'binary'):
> - text += filedata[j]['data']
> + data.append(filedata[j]['data'])
> del filedata[j]['data']
> + # p4 sends 4k chunks, make sure we don't use more than 32 MiB
> + # of additional memory while rebuilding the file data.
> + if len(data) > 8192:
> + text += ''.join(data)
> + data = []
> j += 1
> + text += ''.join(data)
> + del data
>
> if not stat.has_key('depotFile'):
> sys.stderr.write("p4 print fails with: %s\n" % repr(stat))
>
> --
> Sam.
> --
> 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: [PATCH] git-p4: improve performance with large files
From: Han-Wen Nienhuys @ 2009-03-06 1:25 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Simon Hausmann, git, Sam Hocevar
In-Reply-To: <7vzlfzwiyn.fsf@gitster.siamese.dyndns.org>
I don't understand the point of trying to save the 32 mb, if people
are sending you blobs that are that large.
The approach to avoid sequences of appends looks sound.
On Thu, Mar 5, 2009 at 10:14 PM, Junio C Hamano <gitster@pobox.com> wrote:
>> ... The ideal solution is to use a generator and refactor the commit
>> handling as a stream. I am working on that but it involves deeper
>> changes, so as I am not sure it will be accepted, I'm providing the
>> attached compromise patch first. At least it solves the appaling speed
>> issue. I tuned it so that it never uses more than 32 MiB extra memory.
>> + text += ''.join(data)
>> + del data
i'd say
data = []
add a comment that you're trying to save memory. There is no reason to
remove data from the namespace.
--
Han-Wen Nienhuys
Google Engineering Belo Horizonte
hanwen@google.com
^ permalink raw reply
* Re: Subject: [PATCH] Push to create
From: Miles Bader @ 2009-03-06 1:37 UTC (permalink / raw)
To: git
In-Reply-To: <20090304175830.GA6305@mit.edu>
Theodore Tso <tytso@mit.edu> writes:
> One of the advantages that bzr has is that it is integrated fairly
> tightly with Launchpad
To be honest, that seems vaguely creepy...
-Miles
--
Friendship, n. A ship big enough to carry two in fair weather, but only one
in foul.
^ permalink raw reply
* Re: orthogonal cases of log --date option
From: Miles Bader @ 2009-03-06 1:47 UTC (permalink / raw)
To: git
In-Reply-To: <20090305104304.GA17760@coredump.intra.peff.net>
Jeff King <peff@peff.net> writes:
> git log --local-dates --date=default
>
> This makes the interface simpler to understand: --date remains a
> selector, and --date=local is a special case that new people don't need
> to think about or understand.
I agree, a separate option is more clear.
I suppose (as Junio later commented) that an explicit "--date-tz" option
might be cool too, though I suppose 99% of the time, people would only
ever specify "--date-tz=local" or "--date-gz=gmt"...
[one use-case I can think of for a more general option would be
if you want to see a log from somebody else's point of view ... "when
was john in san francisco doing all that hacking?"]
-Miles
--
The trouble with most people is that they think with their hopes or
fears or wishes rather than with their minds. -- Will Durant
^ permalink raw reply
* Re: git push bash completion
From: Jay Soffian @ 2009-03-06 1:57 UTC (permalink / raw)
To: markus.heidelberg; +Cc: Sverre Rabbelier, Git Mailing List
In-Reply-To: <200903060015.39834.markus.heidelberg@web.de>
On Thu, Mar 5, 2009 at 6:15 PM, Markus Heidelberg
<markus.heidelberg@web.de> wrote:
> Something similar happens with fetch and pull. They only complete the
> remote name, when exactly 2 words are existing on the command line
> ("git" and the subcommand) by: if [ "$COMP_CWORD" = 2 ]
>
> Doesn't seem right.
I'm working on a fix. I hope to have it out tonight for review.
j.
^ permalink raw reply
* Re: [ANNOUNCE] TopGit 0.7
From: Mark Wilden @ 2009-03-06 2:04 UTC (permalink / raw)
To: Uwe Kleine-König; +Cc: git
In-Reply-To: <3c30da400903051245j2f89a136m95cdae3c64292024@mail.gmail.com>
> also sprach Mark Wilden <mark@mwilden.com> [2009.03.05.2145 +0100]:
>> When announcing a release, you might consider including a few
>> words about what the project does. There will be people (like me)
>> who were not familiar with it before seeing the release
>> announcement, and this would help them to quickly find out whether
>> it should be investigated.
Oops - I didn't see the description of the project. I stopped reading
before I got there. :)
///ark
^ permalink raw reply
* Re: [PATCH] Documentation - More examples for git bisect
From: John Tapsell @ 2009-03-06 2:50 UTC (permalink / raw)
To: Christian Couder; +Cc: Git Mailing List
In-Reply-To: <200903052244.35702.chriscool@tuxfamily.org>
2009/3/5 Christian Couder <chriscool@tuxfamily.org>:
> Le jeudi 5 mars 2009, John Tapsell a écrit :
>> Including passing parameters to the programs, and running more
>> complicated checks without requiring a seperate shell script.
>>
>> Signed-off-by: John Tapsell
>
> That looks good to me, except perhaps that your signed-off-by has no email
> address, but I don't know if it's a problem or not.
Opps. Signed-off-by: John Tapsell <johnflux@gmail.com>
>
> Acked-by: Christian Couder <chriscool@tuxfamily.org>
>
> Thanks,
> Christian.
>
^ permalink raw reply
* [PATCHv4] Make git-clone respect branch.autosetuprebase
From: Pat Notz @ 2009-03-06 2:58 UTC (permalink / raw)
To: git; +Cc: Pat Notz
In-Reply-To: <7vsklt94ws.fsf@gitster.siamese.dyndns.org>
When git-clone creates an initial branch it was not
checking the branch.autosetuprebase configuration
option (which may exist in ~/.gitconfig).
Create a single function for installing a branch into
the config file. Both 'clone' and 'branch' now use the
same function for installing branches.
Refactored as suggested by Junio C. Hamano.
Signed-off-by: Pat Notz <pknotz@sandia.gov>
---
branch.c | 54 ++++++++++++++++++++++++++++++++++++++----------------
branch.h | 11 +++++++++++
builtin-clone.c | 24 ++++++++----------------
t/t5601-clone.sh | 15 +++++++++++++++
4 files changed, 72 insertions(+), 32 deletions(-)
diff --git a/branch.c b/branch.c
index 1f00e44..332223b 100644
--- a/branch.c
+++ b/branch.c
@@ -32,21 +32,52 @@ static int find_tracked_branch(struct remote *remote, void *priv)
return 0;
}
-static int should_setup_rebase(const struct tracking *tracking)
+static int should_setup_rebase(const char * origin)
{
switch (autorebase) {
case AUTOREBASE_NEVER:
return 0;
case AUTOREBASE_LOCAL:
- return tracking->remote == NULL;
+ return origin == NULL;
case AUTOREBASE_REMOTE:
- return tracking->remote != NULL;
+ return origin != NULL;
case AUTOREBASE_ALWAYS:
return 1;
}
return 0;
}
+void install_branch_config(int verbose_flag,
+ const char *local,
+ const char *origin,
+ const char *remote)
+{
+ struct strbuf key = STRBUF_INIT;
+ int rebasing = should_setup_rebase(origin);
+
+ strbuf_addf(&key, "branch.%s.remote", local);
+ git_config_set(key.buf, origin ? origin : ".");
+
+ strbuf_reset(&key);
+ strbuf_addf(&key, "branch.%s.merge", local);
+ git_config_set(key.buf, remote);
+
+ if (rebasing) {
+ strbuf_reset(&key);
+ strbuf_addf(&key, "branch.%s.rebase", local);
+ git_config_set(key.buf, "true");
+ }
+
+ if (verbose_flag & BRANCH_CONFIG_VERBOSE)
+ printf("Branch %s set up to track %s branch %s %s.\n",
+ local,
+ origin ? "remote" : "local",
+ remote,
+ rebasing ? "by rebasing" : "by merging");
+ strbuf_release(&key);
+}
+
+
/*
* This is called when new_ref is branched off of orig_ref, and tries
* to infer the settings for branch.<new_ref>.{remote,merge} from the
@@ -55,7 +86,6 @@ static int should_setup_rebase(const struct tracking *tracking)
static int setup_tracking(const char *new_ref, const char *orig_ref,
enum branch_track track)
{
- char key[1024];
struct tracking tracking;
if (strlen(new_ref) > 1024 - 7 - 7 - 1)
@@ -79,18 +109,10 @@ static int setup_tracking(const char *new_ref, const char *orig_ref,
if (tracking.matches > 1)
return error("Not tracking: ambiguous information for ref %s",
orig_ref);
-
- sprintf(key, "branch.%s.remote", new_ref);
- git_config_set(key, tracking.remote ? tracking.remote : ".");
- sprintf(key, "branch.%s.merge", new_ref);
- git_config_set(key, tracking.src ? tracking.src : orig_ref);
- printf("Branch %s set up to track %s branch %s.\n", new_ref,
- tracking.remote ? "remote" : "local", orig_ref);
- if (should_setup_rebase(&tracking)) {
- sprintf(key, "branch.%s.rebase", new_ref);
- git_config_set(key, "true");
- printf("This branch will rebase on pull.\n");
- }
+ install_branch_config(BRANCH_CONFIG_VERBOSE,
+ new_ref,
+ tracking.remote,
+ tracking.src ? tracking.src : orig_ref);
free(tracking.src);
return 0;
diff --git a/branch.h b/branch.h
index 9f0c2a2..9f7fdb0 100644
--- a/branch.h
+++ b/branch.h
@@ -21,4 +21,15 @@ void create_branch(const char *head, const char *name, const char *start_name,
*/
void remove_branch_state(void);
+/*
+ * Configure local branch "local" to merge remote branch "remote"
+ * taken from origin "origin".
+ */
+#define BRANCH_CONFIG_QUIET 00
+#define BRANCH_CONFIG_VERBOSE 01
+void install_branch_config(int verbose_flag,
+ const char *local,
+ const char *origin,
+ const char *remote);
+
#endif
diff --git a/builtin-clone.c b/builtin-clone.c
index c338910..e1abf83 100644
--- a/builtin-clone.c
+++ b/builtin-clone.c
@@ -17,6 +17,7 @@
#include "unpack-trees.h"
#include "transport.h"
#include "strbuf.h"
+#include "branch.h"
#include "dir.h"
#include "pack-refs.h"
#include "sigchain.h"
@@ -350,19 +351,6 @@ static struct ref *write_remote_refs(const struct ref *refs,
return local_refs;
}
-static void install_branch_config(const char *local,
- const char *origin,
- const char *remote)
-{
- struct strbuf key = STRBUF_INIT;
- strbuf_addf(&key, "branch.%s.remote", local);
- git_config_set(key.buf, origin);
- strbuf_reset(&key);
- strbuf_addf(&key, "branch.%s.merge", local);
- git_config_set(key.buf, remote);
- strbuf_release(&key);
-}
-
int cmd_clone(int argc, const char **argv, const char *prefix)
{
int use_local_hardlinks = 1;
@@ -553,8 +541,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
remote_head = NULL;
option_no_checkout = 1;
if (!option_bare)
- install_branch_config("master", option_origin,
- "refs/heads/master");
+ install_branch_config(BRANCH_CONFIG_QUIET,
+ "master",
+ option_origin,
+ "refs/heads/master");
}
if (head_points_at) {
@@ -583,7 +573,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
head_points_at->peer_ref->name,
reflog_msg.buf);
- install_branch_config(head, option_origin,
+ install_branch_config(BRANCH_CONFIG_QUIET,
+ head,
+ option_origin,
head_points_at->name);
}
} else if (remote_head) {
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index 44793f2..fa10573 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -159,4 +159,19 @@ test_expect_success 'clone a void' '
test_cmp target-6/.git/config target-7/.git/config
'
+test_expect_success 'clone respects global branch.autosetuprebase' '
+ (
+ HOME="`pwd`" &&
+ export HOME &&
+ test_config="$HOME"/.gitconfig &&
+ unset GIT_CONFIG_NOGLOBAL &&
+ git config -f "$test_config" branch.autosetuprebase remote &&
+ rm -fr dst &&
+ git clone src dst &&
+ cd dst &&
+ actual="z$(git config branch.master.rebase)" &&
+ test ztrue = $actual
+ )
+'
+
test_done
--
1.6.1.2
^ permalink raw reply related
* setting up tracking on push
From: Miles Bader @ 2009-03-06 3:07 UTC (permalink / raw)
To: git
[...sorry if this is a dup -- my previous attempt didn't seem to take...]
Is there an "easy" way to set up tracking for a branch which starts out
locally, and is eventually pushed to a remote?
E.g., I create a new local branch "grognul", and then later propagate it
to my remote site using:
git push origin grognul
That works great (creating origin/grognul), but to set up tracking, I
currently edit .git/config (not insanely difficult, but vaguely
annoying). This is as opposed to a branch which starts out remotely,
and is pulled, where one can just use "git branch --track" or "git
checkout --track" (or use a config option to do so by default).
I vaguely feel like I should be able to do:
git push --track origin grognul
[I do this particular action -- creating a branch locally and then
pushing it to origin -- very very often, thus my desire for a handy
option.]
What do other people think?
-miles
--
Inhumanity, n. One of the signal and characteristic qualities of humanity.
^ permalink raw reply
* Re: setting up tracking on push
From: John Tapsell @ 2009-03-06 3:17 UTC (permalink / raw)
To: Miles Bader; +Cc: git
In-Reply-To: <buofxhr2vta.fsf@dhlpc061.dev.necel.com>
2009/3/6 Miles Bader <miles@gnu.org>:
> [...sorry if this is a dup -- my previous attempt didn't seem to take...]
>
> Is there an "easy" way to set up tracking for a branch which starts out
> locally, and is eventually pushed to a remote?
>
> E.g., I create a new local branch "grognul", and then later propagate it
> to my remote site using:
>
> git push origin grognul
>
> That works great (creating origin/grognul), but to set up tracking, I
> currently edit .git/config (not insanely difficult, but vaguely
> annoying). This is as opposed to a branch which starts out remotely,
> and is pulled, where one can just use "git branch --track" or "git
> checkout --track" (or use a config option to do so by default).
>
> I vaguely feel like I should be able to do:
>
> git push --track origin grognul
>
> [I do this particular action -- creating a branch locally and then
> pushing it to origin -- very very often, thus my desire for a handy
> option.]
>
> What do other people think?
I got bitten by this too, and there's no clue given how to set up the
tracking. I'd like it to even ask the user when pushing:
$ git push origin grognul
Pushing blah blah...
Do you want to track this? [Y/N/?]
:-)
John
>
> -miles
>
> --
> Inhumanity, n. One of the signal and characteristic qualities of humanity.
> --
> 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
* [PATCH 0/3] improve bash completion of fetch, pull, and push
From: Jay Soffian @ 2009-03-06 4:39 UTC (permalink / raw)
To: git
Cc: Jay Soffian, Markus Heidelberg, Sverre Rabbelier, Junio C Hamano,
Shawn O . Pearce
On Thu, Mar 5, 2009 at 6:15 PM, Markus Heidelberg <markus.heidelberg@web.de> wrote:
> Sverre Rabbelier, 05.03.2009:
>> Heya,
>>
>> Observe:
>> $ git push ori<tab>
>> git push origin
>>
>> $ git push -f ori<tab>
>> git push -f origin/
>>
>> Something weird going on there, or is this intentional and am I
>> missing something?
>
> Something similar happens with fetch and pull. They only complete the
> remote name, when exactly 2 words are existing on the command line
> ("git" and the subcommand) by: if [ "$COMP_CWORD" = 2 ]
>
> Doesn't seem right.
This series is intended to fix the original issue, as well as provide
--option completion for all three commands. And, I made a clean spot, so
I had to clean up a couple other things.
Jay Soffian (3):
bash completion: fix completion issues with fetch, pull, and push
bash completion: refactor --strategy completion
bash completion: teach fetch, pull, and push to complete their
options
contrib/completion/git-completion.bash | 197 +++++++++++++++++++++-----------
1 files changed, 130 insertions(+), 67 deletions(-)
^ permalink raw reply
* [PATCH 1/3] bash completion: fix completion issues with fetch, pull, and push
From: Jay Soffian @ 2009-03-06 4:39 UTC (permalink / raw)
To: git
Cc: Jay Soffian, Markus Heidelberg, Sverre Rabbelier, Junio C Hamano,
Shawn O . Pearce
In-Reply-To: <cover.1236314073.git.jaysoffian@gmail.com>
Sverre Rabbelier noticed a completion issue with push:
$ git push ori<tab>
git push origin
$ git push -f ori<tab>
git push -f origin/
Markus Heidelberg pointed out that the issue extends to fetch and pull.
The reason is that the current code naively assumes that if
COMP_CWORD=2, it should complete a remote name, otherwise it should
complete a refspec. This assumption fails if there are any --options.
This patch fixes that issue by instead scanning COMP_CWORDS to see if
the remote has been completed yet (we now assume the first non-dashed
argument is the remote). The new logic is factored into a function,
shared by fetch, pull, and push.
The new function also properly handles '.' as the remote.
Signed-off-by: Jay Soffian <jaysoffian@gmail.com>
---
contrib/completion/git-completion.bash | 109 +++++++++++++++++--------------
1 files changed, 60 insertions(+), 49 deletions(-)
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 0a3092f..b347fdd 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -383,6 +383,63 @@ __git_complete_revlist ()
esac
}
+__git_complete_remote_or_refspec ()
+{
+ local cmd="${COMP_WORDS[1]}"
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ local i c=2 remote="" pfx="" lhs=1
+ while [ $c -lt $COMP_CWORD ]; do
+ i="${COMP_WORDS[c]}"
+ case "$i" in
+ -*) ;;
+ *) remote="$i"; break ;;
+ esac
+ c=$((++c))
+ done
+ if [ -z "$remote" ]; then
+ __gitcomp "$(__git_remotes)"
+ return
+ fi
+ [ "$remote" = "." ] && remote=
+ case "$cur" in
+ *:*)
+ case "$COMP_WORDBREAKS" in
+ *:*) : great ;;
+ *) pfx="${cur%%:*}:" ;;
+ esac
+ cur="${cur#*:}"
+ lhs=0
+ ;;
+ +*)
+ pfx="+"
+ cur="${cur#+}"
+ ;;
+ esac
+ case "$cmd" in
+ fetch)
+ if [ $lhs = 1 ]; then
+ __gitcomp "$(__git_refs2 "$remote")" "$pfx" "$cur"
+ else
+ __gitcomp "$(__git_refs)" "$pfx" "$cur"
+ fi
+ ;;
+ pull)
+ if [ $lhs = 1 ]; then
+ __gitcomp "$(__git_refs "$remote")" "$pfx" "$cur"
+ else
+ __gitcomp "$(__git_refs)" "$pfx" "$cur"
+ fi
+ ;;
+ push)
+ if [ $lhs = 1 ]; then
+ __gitcomp "$(__git_refs)" "$pfx" "$cur"
+ else
+ __gitcomp "$(__git_refs "$remote")" "$pfx" "$cur"
+ fi
+ ;;
+ esac
+}
+
__git_all_commands ()
{
if [ -n "$__git_all_commandlist" ]; then
@@ -828,25 +885,7 @@ _git_diff ()
_git_fetch ()
{
- local cur="${COMP_WORDS[COMP_CWORD]}"
-
- if [ "$COMP_CWORD" = 2 ]; then
- __gitcomp "$(__git_remotes)"
- else
- case "$cur" in
- *:*)
- local pfx=""
- case "$COMP_WORDBREAKS" in
- *:*) : great ;;
- *) pfx="${cur%%:*}:" ;;
- esac
- __gitcomp "$(__git_refs)" "$pfx" "${cur#*:}"
- ;;
- *)
- __gitcomp "$(__git_refs2 "${COMP_WORDS[2]}")"
- ;;
- esac
- fi
+ __git_complete_remote_or_refspec
}
_git_format_patch ()
@@ -1111,40 +1150,12 @@ _git_name_rev ()
_git_pull ()
{
- local cur="${COMP_WORDS[COMP_CWORD]}"
-
- if [ "$COMP_CWORD" = 2 ]; then
- __gitcomp "$(__git_remotes)"
- else
- __gitcomp "$(__git_refs "${COMP_WORDS[2]}")"
- fi
+ __git_complete_remote_or_refspec
}
_git_push ()
{
- local cur="${COMP_WORDS[COMP_CWORD]}"
-
- if [ "$COMP_CWORD" = 2 ]; then
- __gitcomp "$(__git_remotes)"
- else
- case "$cur" in
- *:*)
- local pfx=""
- case "$COMP_WORDBREAKS" in
- *:*) : great ;;
- *) pfx="${cur%%:*}:" ;;
- esac
-
- __gitcomp "$(__git_refs "${COMP_WORDS[2]}")" "$pfx" "${cur#*:}"
- ;;
- +*)
- __gitcomp "$(__git_refs)" + "${cur#+}"
- ;;
- *)
- __gitcomp "$(__git_refs)"
- ;;
- esac
- fi
+ __git_complete_remote_or_refspec
}
_git_rebase ()
--
1.6.2.rc2.332.g5d21b
^ permalink raw reply related
* [PATCH 2/3] bash completion: refactor --strategy completion
From: Jay Soffian @ 2009-03-06 4:39 UTC (permalink / raw)
To: git
Cc: Jay Soffian, Markus Heidelberg, Sverre Rabbelier, Junio C Hamano,
Shawn O . Pearce
In-Reply-To: <cover.1236314073.git.jaysoffian@gmail.com>
The code to complete --strategy was duplicated between _git_rebase and
_git_merge, and is about to gain a third caller (_git_pull). This patch
factors it into its own function.
Signed-off-by: Jay Soffian <jaysoffian@gmail.com>
---
contrib/completion/git-completion.bash | 37 ++++++++++++++++---------------
1 files changed, 19 insertions(+), 18 deletions(-)
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index b347fdd..8924185 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -440,6 +440,22 @@ __git_complete_remote_or_refspec ()
esac
}
+__git_complete_strategy ()
+{
+ case "${COMP_WORDS[COMP_CWORD-1]}" in
+ -s|--strategy)
+ __gitcomp "$(__git_merge_strategies)"
+ return 1
+ esac
+ case "$cur" in
+ --strategy=*)
+ __gitcomp "$(__git_merge_strategies)" "" "${cur##--strategy=}"
+ return 1
+ ;;
+ esac
+ return 0
+}
+
__git_all_commands ()
{
if [ -n "$__git_all_commandlist" ]; then
@@ -1086,17 +1102,10 @@ _git_log ()
_git_merge ()
{
+ __git_complete_strategy && return
+
local cur="${COMP_WORDS[COMP_CWORD]}"
- case "${COMP_WORDS[COMP_CWORD-1]}" in
- -s|--strategy)
- __gitcomp "$(__git_merge_strategies)"
- return
- esac
case "$cur" in
- --strategy=*)
- __gitcomp "$(__git_merge_strategies)" "" "${cur##--strategy=}"
- return
- ;;
--*)
__gitcomp "
--no-commit --no-stat --log --no-log --squash --strategy
@@ -1165,16 +1174,8 @@ _git_rebase ()
__gitcomp "--continue --skip --abort"
return
fi
- case "${COMP_WORDS[COMP_CWORD-1]}" in
- -s|--strategy)
- __gitcomp "$(__git_merge_strategies)"
- return
- esac
+ __git_complete_strategy && return
case "$cur" in
- --strategy=*)
- __gitcomp "$(__git_merge_strategies)" "" "${cur##--strategy=}"
- return
- ;;
--*)
__gitcomp "--onto --merge --strategy --interactive"
return
--
1.6.2.rc2.332.g5d21b
^ permalink raw reply related
* [PATCH 3/3] bash completion: teach fetch, pull, and push to complete their options
From: Jay Soffian @ 2009-03-06 4:39 UTC (permalink / raw)
To: git
Cc: Jay Soffian, Markus Heidelberg, Sverre Rabbelier, Junio C Hamano,
Shawn O . Pearce
In-Reply-To: <cover.1236314073.git.jaysoffian@gmail.com>
fetch, pull, and push didn't know their options. They do now. merge's
options are factored into a variable so they can be shared between
_git_merge and _git_pull
Signed-off-by: Jay Soffian <jaysoffian@gmail.com>
---
contrib/completion/git-completion.bash | 61 +++++++++++++++++++++++++++++---
1 files changed, 56 insertions(+), 5 deletions(-)
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 8924185..3ebedea 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -387,10 +387,11 @@ __git_complete_remote_or_refspec ()
{
local cmd="${COMP_WORDS[1]}"
local cur="${COMP_WORDS[COMP_CWORD]}"
- local i c=2 remote="" pfx="" lhs=1
+ local i c=2 remote="" pfx="" lhs=1 no_complete_refspec=0
while [ $c -lt $COMP_CWORD ]; do
i="${COMP_WORDS[c]}"
case "$i" in
+ --all|--mirror) [ "$cmd" = "push" ] && no_complete_refspec=1 ;;
-*) ;;
*) remote="$i"; break ;;
esac
@@ -400,6 +401,10 @@ __git_complete_remote_or_refspec ()
__gitcomp "$(__git_remotes)"
return
fi
+ if [ $no_complete_refspec = 1 ]; then
+ COMPREPLY=()
+ return
+ fi
[ "$remote" = "." ] && remote=
case "$cur" in
*:*)
@@ -899,8 +904,20 @@ _git_diff ()
__git_complete_file
}
+__git_fetch_options="
+ --quiet --verbose --append --upload-pack --force --keep --depth=
+ --tags --no-tags
+"
+
_git_fetch ()
{
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ case "$cur" in
+ --*)
+ __gitcomp "$__git_fetch_options"
+ return
+ ;;
+ esac
__git_complete_remote_or_refspec
}
@@ -1100,6 +1117,11 @@ _git_log ()
__git_complete_revlist
}
+__git_merge_options="
+ --no-commit --no-stat --log --no-log --squash --strategy
+ --commit --stat --no-squash --ff --no-ff
+"
+
_git_merge ()
{
__git_complete_strategy && return
@@ -1107,10 +1129,7 @@ _git_merge ()
local cur="${COMP_WORDS[COMP_CWORD]}"
case "$cur" in
--*)
- __gitcomp "
- --no-commit --no-stat --log --no-log --squash --strategy
- --commit --stat --no-squash --ff --no-ff
- "
+ __gitcomp "$__git_merge_options"
return
esac
__gitcomp "$(__git_refs)"
@@ -1159,11 +1178,43 @@ _git_name_rev ()
_git_pull ()
{
+ __git_complete_strategy && return
+
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ case "$cur" in
+ --*)
+ __gitcomp "
+ --rebase --no-rebase
+ $__git_merge_options
+ $__git_fetch_options
+ "
+ return
+ ;;
+ esac
__git_complete_remote_or_refspec
}
_git_push ()
{
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ case "${COMP_WORDS[COMP_CWORD-1]}" in
+ --repo)
+ __gitcomp "$(__git_remotes)"
+ return
+ esac
+ case "$cur" in
+ --repo=*)
+ __gitcomp "$(__git_remotes)" "" "${cur##--repo=}"
+ return
+ ;;
+ --*)
+ __gitcomp "
+ --all --mirror --tags --dry-run --force --verbose
+ --receive-pack= --repo=
+ "
+ return
+ ;;
+ esac
__git_complete_remote_or_refspec
}
--
1.6.2.rc2.332.g5d21b
^ permalink raw reply related
* Re: setting up tracking on push
From: Jay Soffian @ 2009-03-06 4:49 UTC (permalink / raw)
To: Miles Bader; +Cc: git
In-Reply-To: <buofxhr2vta.fsf@dhlpc061.dev.necel.com>
On Thu, Mar 5, 2009 at 10:07 PM, Miles Bader <miles@gnu.org> wrote:
> I vaguely feel like I should be able to do:
>
> git push --track origin grognul
Hmm, I vaguely remember seeing a patch come across the list to do this.
j.
^ permalink raw reply
* [PATCH 0/5] Extend pattern refspecs
From: Daniel Barkalow @ 2009-03-06 4:56 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
This series only supports the narrowest case of having the * in the middle
of a side of a refspec: having it as a full path component on each side.
Patches 1-3 centralize all of the parsing and matching rules to a pair of
functions; patch 4 makes the stored representation more convenient (and
serves as a distinguished bisection outcome for anything I missed that was
relying on the contents of struct refspec for patterns); and patch 5
extends the matching implementation and loosens the ref format
requirements to allow the * to be in the middle.
An easy followup would relax the restrictions further without requiring
any particularly tricky further changes.
Daniel Barkalow (5):
Make clone parse the default fetch refspec with the regular code
Use a single function to match names against patterns
Use the matching function to generate the match results
Keep '*' in pattern refspecs
Support '*' in the middle of a refspec
builtin-clone.c | 25 ++++++++--------
refs.c | 15 +++++----
remote.c | 78 +++++++++++++++++++++++++++++----------------------
t/t5511-refspec.sh | 12 ++++++++
4 files changed, 77 insertions(+), 53 deletions(-)
^ permalink raw reply
* [PATCH 1/5] Make clone parse the default refspec with the normal code
From: Daniel Barkalow @ 2009-03-06 4:56 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
Instead of creating a refspec by hand, go through the refspec parsing
code, so that changes in the refspec storage will be accounted for.
Signed-off-by: Daniel Barkalow <barkalow@iabervon.org>
---
builtin-clone.c | 25 +++++++++++++------------
1 files changed, 13 insertions(+), 12 deletions(-)
diff --git a/builtin-clone.c b/builtin-clone.c
index c338910..06b5a7f 100644
--- a/builtin-clone.c
+++ b/builtin-clone.c
@@ -378,7 +378,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
struct transport *transport = NULL;
char *src_ref_prefix = "refs/heads/";
- struct refspec refspec;
+ struct refspec *refspec;
+ const char *fetch_pattern;
junk_pid = getpid();
@@ -487,8 +488,14 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
strbuf_addf(&branch_top, "refs/remotes/%s/", option_origin);
}
+ strbuf_addf(&value, "+%s*:%s*", src_ref_prefix, branch_top.buf);
+
if (option_mirror || !option_bare) {
/* Configure the remote */
+ strbuf_addf(&key, "remote.%s.fetch", option_origin);
+ git_config_set_multivar(key.buf, value.buf, "^$", 0);
+ strbuf_reset(&key);
+
if (option_mirror) {
strbuf_addf(&key, "remote.%s.mirror", option_origin);
git_config_set(key.buf, "true");
@@ -497,19 +504,13 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
strbuf_addf(&key, "remote.%s.url", option_origin);
git_config_set(key.buf, repo);
- strbuf_reset(&key);
-
- strbuf_addf(&key, "remote.%s.fetch", option_origin);
- strbuf_addf(&value, "+%s*:%s*", src_ref_prefix, branch_top.buf);
- git_config_set_multivar(key.buf, value.buf, "^$", 0);
strbuf_reset(&key);
- strbuf_reset(&value);
}
- refspec.force = 0;
- refspec.pattern = 1;
- refspec.src = src_ref_prefix;
- refspec.dst = branch_top.buf;
+ fetch_pattern = value.buf;
+ refspec = parse_fetch_refspec(1, &fetch_pattern);
+
+ strbuf_reset(&value);
if (path && !is_bundle)
refs = clone_local(path, git_dir);
@@ -543,7 +544,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
if (refs) {
clear_extra_refs();
- mapped_refs = write_remote_refs(refs, &refspec, reflog_msg.buf);
+ mapped_refs = write_remote_refs(refs, refspec, reflog_msg.buf);
head_points_at = locate_head(refs, mapped_refs, &remote_head);
}
--
1.6.1.286.gd33a4.dirty
^ permalink raw reply related
* [PATCH 2/5] Use a single function to match names against patterns
From: Daniel Barkalow @ 2009-03-06 4:56 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
This will help when the matching changes.
Signed-off-by: Daniel Barkalow <barkalow@iabervon.org>
---
remote.c | 12 +++++++++---
1 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/remote.c b/remote.c
index d7079c6..709300b 100644
--- a/remote.c
+++ b/remote.c
@@ -719,6 +719,12 @@ int remote_has_url(struct remote *remote, const char *url)
return 0;
}
+static int name_fits_pattern(const char *key, const char *name)
+{
+ int ret = !prefixcmp(key, name);
+ return ret;
+}
+
int remote_find_tracking(struct remote *remote, struct refspec *refspec)
{
int find_src = refspec->src == NULL;
@@ -742,7 +748,7 @@ int remote_find_tracking(struct remote *remote, struct refspec *refspec)
if (!fetch->dst)
continue;
if (fetch->pattern) {
- if (!prefixcmp(needle, key)) {
+ if (name_fits_pattern(key, needle)) {
*result = xmalloc(strlen(value) +
strlen(needle) -
strlen(key) + 1);
@@ -1020,7 +1026,7 @@ static const struct refspec *check_pattern_match(const struct refspec *rs,
continue;
}
- if (rs[i].pattern && !prefixcmp(src->name, rs[i].src))
+ if (rs[i].pattern && name_fits_pattern(rs[i].src, src->name))
return rs + i;
}
if (matching_refs != -1)
@@ -1160,7 +1166,7 @@ static struct ref *get_expanded_map(const struct ref *remote_refs,
for (ref = remote_refs; ref; ref = ref->next) {
if (strchr(ref->name, '^'))
continue; /* a dereference item */
- if (!prefixcmp(ref->name, refspec->src)) {
+ if (name_fits_pattern(refspec->src, ref->name)) {
const char *match;
struct ref *cpy = copy_ref(ref);
match = ref->name + remote_prefix_len;
--
1.6.1.286.gd33a4.dirty
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox