Git development
 help / color / mirror / Atom feed
* Re: [Newbie] How to *actually* get rid of remote tracking branch?
From: Jakub Narebski @ 2007-11-13 14:53 UTC (permalink / raw)
  To: git
In-Reply-To: <874pfq9q8s.fsf@osv.gnss.ru>

[Cc: Sergei Organov <osv@javad.com>, git@vger.kernel.org]
Please CC git mailing list, git@vger.kernel.org

Sergei Organov wrote:

> Hello,
> 
> I want to get rid of origin/pu remote tracking branch. What do I do? I
> RTFM git-branch. What does it suggest?
> 
> git branch -d -r origin/pu
> 
> So far so good. However, it doesn't seem to work in practice:
 

> $ git branch -d -r origin/pu
> Deleted remote branch origin/pu.
> $ git remote show origin
> * remote origin
>   URL: git://git.kernel.org/pub/scm/git/git.git
>   Remote branch(es) merged with 'git pull' while on branch master
>     master
>   New remote branches (next fetch will store in remotes/origin)
>     pu
>   ^^^^^^^^^^^^^^^^^^^ What???  
>   Tracked remote branches
>     html maint man master next todo

Check out what do you have in .git/config file, in the [remote "origin"]
section. Most probably (if you cloned this repository using new enough git)
you have wildcard refspec there, which means that git would pick all new
branches when fetching / pulling from given repository. The wildcard
refspec is not documented adequately, so I'm not sure if adding

        fetch = !refs/heads/pu

would help, or do you have to replace wildcard refspec by explicit list of
branches you want to fetch.

-- 
Jakub Narebski
Warsaw, Poland
ShadeHawk on #git

^ permalink raw reply

* Re: git 1.5.3.5 error over NFS
From: Bill Lear @ 2007-11-13 14:49 UTC (permalink / raw)
  To: Alex Riesen; +Cc: git
In-Reply-To: <20071112233309.GI2918@steel.home>

On Tuesday, November 13, 2007 at 00:33:09 (+0100) Alex Riesen writes:
>This is the almost same message I cced to lk and nfs, but with
>Git-interasting parts added.
>
>Bill Lear, Mon, Nov 12, 2007 16:39:15 +0100:
>> On Saturday, November 10, 2007 at 00:21:06 (+0100) Alex Riesen writes:
>> >Bill Lear, Fri, Nov 09, 2007 16:31:39 +0100:
>> >> I've brought this up before, but I don't recall a resolution to it.
>> >> 
>> >> We have an NFS-mounted filesystem, and git pull is choking on it.
>> >> 
>> >> % uname -a
>> >> Linux uhlr.zopyra.com 2.6.9-42.0.2.ELsmp #1 SMP Wed Aug 23 13:38:27 BST 2006 x86_64 x86_64 x86_64 GNU/Linux
>
>It is a really old kernel... Maybe you could try with some of the
>recent ones?

I'll see if we can: the machine in question is a high security one,
and not easy to change.  We haven't seen this sort of problem
elsewhere on our newer systems as far as I know.

>I extend the part you quoted. The file is opened here:
>...
>This is strange. The current git should not produce anything like
>this (and does not, here). ...

You are absolutely correct.  My comrade ran this with 1.5.0.1 by
mistake.  He reran the strace with 1.5.3.5, and I have replaced
the tarball on my server:

    http://www.zopyra.com/~rael/git/git-trace.tar.bz2

With this minor correction (!), here is the last part from
the last file (strace.out.26001):

[...]
write(2, "\n", 1)                       = 1
write(3, "\335\365\205\262RY\360=^h\357\372\274\374_\177\317\357"..., 3712) = 3712
fstat(0, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
open("/etc/mtab", O_RDONLY)             = 4
fstat(4, {st_mode=S_IFREG|0644, st_size=503, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x2a955fa000
read(4, "/dev/sda1 / ext3 rw 0 0\nnone /pr"..., 4096) = 503
close(4)                                = 0
munmap(0x2a955fa000, 4096)              = 0
open("/proc/meminfo", O_RDONLY)         = 4
fstat(4, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x2a955fa000
read(4, "MemTotal:     16396892 kB\nMemFre"..., 1024) = 672
close(4)                                = 0
munmap(0x2a955fa000, 4096)              = 0
write(2, "Resolving 562 deltas...\n", 24) = 24
rt_sigaction(SIGALRM, {0x40f610, [], SA_RESTORER|SA_RESTART, 0x32c512e2b0}, NULL, 8) = 0
setitimer(ITIMER_REAL, {it_interval={1, 0}, it_value={1, 0}}, NULL) = 0
pread(3, "", 242, 1268)                 = 0
write(2, "fatal: ", 7)                  = 7
write(2, "cannot pread pack file: No such "..., 49) = 49
write(2, "\n", 1)                       = 1
exit_group(128)                         = ?

Sorry for the mistake.  I think Linus once commented on an alarm
that he found curious in a previous strace I sent for this some
time ago ...


Bill

^ permalink raw reply

* Re: The 5th issue of the msysGit Herald
From: Johannes Schindelin @ 2007-11-13 14:41 UTC (permalink / raw)
  To: Jakub Narebski; +Cc: git
In-Reply-To: <fhcat4$ef$1@ger.gmane.org>

Hi,

[please Cc: me...]

On Tue, 13 Nov 2007, Jakub Narebski wrote:

> Is this info (and previous issues of "msysGit Herald") available 
> somewhere on the net?

Yes.  It is on the Wiki, too, and there is the "herald" branch in 
msysgit/historical-msysgit.git on repo.or.cz.

Hth,
Dscho

^ permalink raw reply

* Re: [BUG] fast-import producing very deep tree deltas
From: Brian Downing @ 2007-11-13 14:36 UTC (permalink / raw)
  To: Shawn O. Pearce; +Cc: git
In-Reply-To: <20071113092721.GD14735@spearce.org>

On Tue, Nov 13, 2007 at 04:27:21AM -0500, Shawn O. Pearce wrote:
> Brian, does this fix it?
> 
> So if the tree we reloaded was already at the maximum depth we
> wouldn't know it and make the new tree a delta.  Multiply the
> number of times the branch cache has to swap out the tree times
> max_depth (10) and you get the maximum delta depth of a tree created
> by fast-import.  In Brian's case above the active branch cache had
> to swap the branch out 603/604 times during this import to produce
> a tree with a delta depth of 6035.
> 
> Signed-off-by: Shawn O. Pearce <spearce@spearce.org>

/Much/ better:

git-fast-import statistics:
---------------------------------------------------------------------
Alloc'd objects:     140000
Total objects:       135970 (     62664 duplicates                  )
      blobs  :        42196 (     13695 duplicates      19898 deltas)
      trees  :        72143 (     48969 duplicates      62402 deltas)
      commits:        21631 (         0 duplicates          0 deltas)
      tags   :            0 (         0 duplicates          0 deltas)
Total branches:          10 (         1 loads     )
      marks:        1048576 (     63827 unique    )
      atoms:          18971
Memory total:          8329 KiB
       pools:          2860 KiB
     objects:          5468 KiB
---------------------------------------------------------------------
pack_report: getpagesize()            =       4096
pack_report: core.packedGitWindowSize = 1073741824
pack_report: core.packedGitLimit      = 8589934592
pack_report: pack_used_ctr            =     273071
pack_report: pack_mmap_calls          =      16855
pack_report: pack_open_windows        =         50 /        363
pack_report: pack_mapped              = 8529277175 / 8589933814
---------------------------------------------------------------------

depths: count 135970 total 380519 min 0 max 10 mean 2.80 median 1 std_dev 3.22

In addition, fast-import ran much (probably 10x) faster and with much less
memory usage (last time it peaked around 1GB):

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
13098 bdowning  18   0 8223m  34m 6576 R   72  1.7   0:51.44 git-fast-import

Presumably not having to rebuild the root tree object from a hundreds-deep
delta chain many hundreds of times sped things up a bit.

Acked-by: Brian Downing <bdowning@lavos.net>

-bcd

^ permalink raw reply

* [Newbie] How to *actually* get rid of remote tracking branch?
From: Sergei Organov @ 2007-11-13 14:25 UTC (permalink / raw)
  To: git

Hello,

I want to get rid of origin/pu remote tracking branch. What do I do? I
RTFM git-branch. What does it suggest?

git branch -d -r origin/pu

So far so good. However, it doesn't seem to work in practice:

$ git --version
git version 1.5.3.4
$ git remote show origin
* remote origin
  URL: git://git.kernel.org/pub/scm/git/git.git
  Remote branch(es) merged with 'git pull' while on branch master
    master
  Tracked remote branches
    html maint man master next pu todo
$ git branch -d -r origin/pu
Deleted remote branch origin/pu.
$ git remote show origin
* remote origin
  URL: git://git.kernel.org/pub/scm/git/git.git
  Remote branch(es) merged with 'git pull' while on branch master
    master
  New remote branches (next fetch will store in remotes/origin)
    pu
  ^^^^^^^^^^^^^^^^^^^ What???  
  Tracked remote branches
    html maint man master next todo
$ git fetch
remote: Generating pack...
remote: Done counting 422 objects.
Result has 281 objects.
remote: Deltifying 281 objects...
remote:  100% (281/281) done
Indexing 281 objects...
remote: Total 281 (delta 206), reused 255 (delta 181)
 100% (281/281) done
Resolving 206 deltas...
 100% (206/206) done
63 objects were added to complete this thin pack.
* refs/remotes/origin/pu: storing branch 'pu' of git://git.kernel.org/pub/scm/git/git
  commit: fc07419
$ git remote show origin
* remote origin
  URL: git://git.kernel.org/pub/scm/git/git.git
  Remote branch(es) merged with 'git pull' while on branch master
    master
  Tracked remote branches
    html maint man master next pu todo
$

... and I get those origin/pu back?!

What do I do wrong?

-- 
Sergei.

^ permalink raw reply

* Re: wishlist: git info
From: Jakub Narebski @ 2007-11-13 14:20 UTC (permalink / raw)
  To: git
In-Reply-To: <fhbrll$ceq$1@ger.gmane.org>

Jakub Narebski wrote:
> Thomas Neumann wrote:

> You can always write[*1*] git-info.sh or git-info.perl, and install
> it as git-info in your git installation. And send patches here, to git
> mailing list, for comments.
> 
> Footnotes:
> ----------
> [*1*] Even if one of the most common complaints is "too many user-visible
>       commands".

On the other hand one of the proposed and long requested features is
git-explain, which is meant to show status of git command progress. If you
could incorporate proposed features in git-info (perhaps only with some
option: --state, --explain, -v / --verbose,...) there would be no
complaints about yet another git command.

What git-info / git-explain should show:
 * interrupted fetch / fetch in progress
 * interrupted pull  / pull  in progress
 * interrupted merge / merge in progress
 * interrupted commit, commit --amend, or tag editing
 * rebase in progress (including interactive rebase and rebasing
   using merge engine)
 * git-am (apply mbox) in progress

Perhaps we should also include git-count-objects in git-info, like
git-verify-tag functionality was included in 'git tag --verify'...

-- 
Jakub Narebski
Warsaw, Poland
ShadeHawk on #git

^ permalink raw reply

* Re: [PATCH] user-manual: Talk about tracking third-party snapshots
From: Jakub Narebski @ 2007-11-13 14:07 UTC (permalink / raw)
  To: git
In-Reply-To: <11949569992214-git-send-email-msmith@cbnco.com>

Michael Smith wrote:

> +You can use the gitlink:git-cherry[1] command to display the commit
> +IDs that are only present on your local branch, or only on the remote
> +branch, respectively:

I think git-cherry is deprecated in favor of "git log --left-right" (with
appropriate format, for example '--abbrev-commit --pretty=oneline')

BTW. that means that git-cherry can be removed from git-help output,
I think.

-- 
Jakub Narebski
Warsaw, Poland
ShadeHawk on #git

^ permalink raw reply

* Re: The 5th issue of the msysGit Herald
From: Jakub Narebski @ 2007-11-13 14:04 UTC (permalink / raw)
  To: git
In-Reply-To: <Pine.LNX.4.64.0711130312180.4362@racer.site>

Johannes Schindelin wrote:

> Good morning git land!
> 
> This lovely dark 4am (see http://youtube.com/watch?v=yXi6hg90LUU) is 
> as good an occasion as any to offer to you the 5th issue of the 
> msysGit Herald, the not-quite-biweekly news letter to keep you 
> informed about msysGit, the effort to bring one of the most powerful 
> Source Code Management systems to the poor souls stuck with Windows. 

Is this info (and previous issues of "msysGit Herald") available somewhere
on the net? And I don't mean here git mailing list archives, or Google
Groups web interface...

Perhaps it is time for git to have besides Git Homepage and Git Wiki also
Git Blog or Git Feed (with announcements, what's in... and herald). What do
you think?

-- 
Jakub Narebski
Warsaw, Poland
ShadeHawk on #git

^ permalink raw reply

* [PATCH] user-manual: Talk about tracking third-party snapshots
From: Michael Smith @ 2007-11-13 12:29 UTC (permalink / raw)
  To: git; +Cc: gitster, Michael Smith

Add some sections about tracking third-party sources to the advanced
branching chapter. This might save some guesswork for tasks like
importing snapshots and tracking local changes.

Signed-off-by: Michael Smith <msmith@cbnco.com>
---

Years of heavy CVS abuse left me partly brain damaged, and some things that
are pretty easy in Git seemed like they should have been more complicated.
Hopefully this should make it painfully obvious how do to the equivalent
of CVS vendor branches.

 Documentation/user-manual.txt |  139 +++++++++++++++++++++++++++++++++++++++++
 1 files changed, 139 insertions(+), 0 deletions(-)

diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index d99adc6..942f851 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -2711,6 +2711,145 @@ gitlink:git-config[1].
 See gitlink:git-config[1] for more details on the configuration
 options mentioned above.
 
+[[tracking-sources]]
+Tracking local changes to third-party sources
+---------------------------------------------
+
+[[tracking-sources-git]]
+Tracking changes when the upstream is a Git repository
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You don't have to do anything special to keep track of your changes to a
+Git project. All you need is a remote tracking branch for the upstream
+repository--either one of the remote branches created by
+gitlink:git-clone[1], or one you created according to
+<<fetching-individual-branches>>.
+
+Given a situation where you've merged the tracking branch's changes into
+your local branch, then made some more changes, as in the diagram below:
+
+................................................
+ o--o--a--c--e--f--g <-- origin/master (remote tracking branch)
+        \     \
+         b--d--m--h--i <-- master
+................................................
+
+You can view all your local changes--b, d, h, and i--with the
+gitlink:git-diff[1] command:
+
+------------------------------------------
+$ git diff origin/master...master
+------------------------------------------
+
+The three-dot `\...` tells gitlink:git-diff[1] to show the changes on the
+master branch since the last common ancestor with origin/master. (If you
+used two dots instead of three, you'd see the entire patch to go from
+origin/master to master, including reversing commits "f" and "g".)
+
+You can use the gitlink:git-cherry[1] command to display the commit
+IDs that are only present on your local branch, or only on the remote
+branch, respectively:
+
+------------------------------------------------
+$ git cherry -v origin/master master
++ 8ed8ff9315e36824e601659b168bbaad5e4d53ca b
++ 2bf7cdf2bef8e6f8b213634ce67dd01cc9e145e0 d
++ 14f97309ca82f742bc42d03fa4619a81973521a9 h
++ 4497730f04ed9849c807f2a5bf8f097f87636d3f i
+$ git cherry -v master origin/master
++ 8cc12a9763279d6f0c913ef47e0a996193aaa1c5 f
++ c793ea90311db286c0e22d227b494f09620aef3d g
+------------------------------------------------
+
+`git show <commit-id>` can display the full log message and patch for you.
+
+[[tracking-sources-snapshots]]
+Tracking changes when the upstream uses snapshots
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To track your changes to projects that aren't using Git, you can commit
+a snapshot or release onto a branch in your repository, then tag it. If
+you are used to vendor branches in CVS, you'll find this is similar,
+although CVS combines the commit, tag, and sometimes merge operations
+into one "import" command. Here's an example:
+
+------------------------------------------------
+$ mkdir project
+$ cd project
+$ git init
+$ cp -a ~/src/project-1.0/* .
+$ git add .
+$ git commit -m "Import Project v1.0"
+$ git tag v1.0
+$ git branch upstream
+------------------------------------------------
+
+Make your changes as usual on the "master" branch, or on topic branches
+that you merge to "master". For the next import, switch to the
+"upstream" branch, commit the new version, then switch back and merge in
+the changes:
+
+------------------------------------------------
+$ git checkout upstream
+$ rm -r *
+$ cp -a ~/src/project-1.1/* .
+      # "git add ." will catch the added and modified files
+$ git add .
+      # the "-a" flag will commit file deletions, too
+$ git commit -a -m "Import Project v1.1"
+$ git tag v1.1
+$ git checkout master
+$ git merge upstream
+------------------------------------------------
+
+If you are publishing your repository, you may also want to push the
+"upstream" branch and your tags.
+
+[[fixing-branch-ancestry]]
+Fixing branch ancestry
+~~~~~~~~~~~~~~~~~~~~~~
+
+Git can only do a sensible merge if it knows about a common ancestor
+between your local changes and the third-party sources. It needs to know
+the commit where your local changes and the third-party sources began to
+diverge--in other words, the last time they were merged. There are some
+cases where Git might not have a record of this merge:
+
+1. You imported CVS or Subversion vendor branch history into Git.
+Sometimes this can produce completely independent master and vendor
+branches with no merging between the two. All the changes are there,
+they just aren't linked by a merge. You can see this in `gitk --all` as
+two parallel development histories.
+2. You've been importing third-party tarballs or snapshots into Git, but
+now the upstream has switched to Git and you want to pull from their new
+repository. As far as Git knows, their branch is completely independent
+from yours, with no common ancestry.
+
+You can fix situations like these by doing a merge that isn't really a
+merge, using the "ours" merge strategy. Look through the history on the
+third-party branch and try to find the exact commit that matches the
+last snapshot you imported. Often there's a tag close to the commit, or
+on the commit, if you're lucky--but don't trust it blindly; check the
+diffs. Check out your local branch and tell Git about the relationship:
+
+------------------------------------------------
+$ git remote add upstreamgit git://upstream.org/project.git
+$ git fetch upstreamgit
+$ git tag
+v1.0
+v1.1
+v1.2
+$ git checkout master
+$ git merge --strategy=ours \
+    -m "Tie old v1.1 into our history by merging with strategy=ours." \
+    v1.1
+------------------------------------------------
+
+You'll see the branches merge together in `gitk --all` or `git
+show-branch master upstreamgit/master`.  Now you'll be able to merge any
+changes from the remote branch since v1.1 with `git merge
+upstreamgit/master`.
+
 
 [[git-concepts]]
 Git concepts
-- 
1.5.3.2.102.ge6eb7

^ permalink raw reply related

* Re: Cloning empty repositories, was Re: What is the idea for bare repositories?
From: Jeff King @ 2007-11-13 11:40 UTC (permalink / raw)
  To: Matthieu Moy
  Cc: Shawn O. Pearce, Junio C Hamano, Johannes Schindelin, Bill Lear,
	Jan Wielemaker, git
In-Reply-To: <vpqpryefmhj.fsf@bauges.imag.fr>

On Tue, Nov 13, 2007 at 11:50:16AM +0100, Matthieu Moy wrote:

> The "git remote add" thing adds this to my .git/config:
> 
> [remote "origin"]
>         url = /tmp/git1
>         fetch = +refs/heads/*:refs/remotes/origin/*
> 
> While clone normally does a bit more:
> 
> [remote "origin"]
>         url = /tmp/git1/.git
>         fetch = +refs/heads/*:refs/remotes/origin/*
> [branch "master"]
>         remote = origin
>         merge = refs/heads/master

Also, git-clone sets up the HEAD symref automagically (you can specify
it manually with git-remote, but git-clone will put it from the remote's
HEAD).

-Peff

^ permalink raw reply

* [PATCH v2 3/3] send-pack: assign remote errors to each ref
From: Jeff King @ 2007-11-13 11:37 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Pierre Habouzit, Daniel Barkalow, Alex Riesen
In-Reply-To: <20071113102500.GA2767@sigill.intra.peff.net>

This lets us show remote errors (e.g., a denied hook) along
with the usual push output. There are two drawbacks to this
change:

  1. cross-referencing the incoming status with the ref list
     is worst case O(n^2) (where n = number of refs); this
     can be fixed with a smarter implementation

  2. the status parsing is not foolproof. We get a line like

         ng refs/heads/master arbitrary msg

     which cannot be parsed unambiguously in the face of
     refnames with spaces. We do a prefix-match so that
     you will only run into problems if you have two refs,
     one of which is a prefix match of the other, and the
     longer having a space right after the prefix.

Signed-off-by: Jeff King <peff@peff.net>
---
 builtin-send-pack.c |   44 ++++++++++++++++++++++++++++++++++++++------
 cache.h             |    2 ++
 2 files changed, 40 insertions(+), 6 deletions(-)

diff --git a/builtin-send-pack.c b/builtin-send-pack.c
index 5c84766..7d466d9 100644
--- a/builtin-send-pack.c
+++ b/builtin-send-pack.c
@@ -146,14 +146,34 @@ static void get_local_heads(void)
 	for_each_ref(one_local_ref, NULL);
 }
 
-static int receive_status(int in)
+static void set_ref_error(struct ref *refs, const char *line) {
+	struct ref *ref;
+
+	for (ref = refs; ref; ref = ref->next) {
+		const char *msg;
+		if (prefixcmp(line, ref->name))
+			continue;
+		msg = line + strlen(ref->name);
+		if (*msg++ != ' ')
+			continue;
+		ref->status = REF_STATUS_REMOTE_REJECT;
+		ref->error = xstrdup(msg);
+		ref->error[strlen(ref->error)-1] = '\0';
+		return;
+	}
+}
+
+/* a return value of -1 indicates that an error occurred,
+ * but we were able to set individual ref errors. A return
+ * value of -2 means we couldn't even get that far. */
+static int receive_status(int in, struct ref *refs)
 {
 	char line[1000];
 	int ret = 0;
 	int len = packet_read_line(in, line, sizeof(line));
 	if (len < 10 || memcmp(line, "unpack ", 7)) {
 		fprintf(stderr, "did not receive status back\n");
-		return -1;
+		return -2;
 	}
 	if (memcmp(line, "unpack ok\n", 10)) {
 		fputs(line, stderr);
@@ -171,7 +191,7 @@ static int receive_status(int in)
 		}
 		if (!memcmp(line, "ok", 2))
 			continue;
-		fputs(line, stderr);
+		set_ref_error(refs, line + 3);
 		ret = -1;
 	}
 	return ret;
@@ -258,6 +278,12 @@ static void print_push_status(const char *dest, struct ref *refs)
 		case REF_STATUS_NONFF:
 			print_ref_status('!', "[rejected]", ref, ref->peer_ref, "non-fast forward");
 			break;
+		case REF_STATUS_REMOTE_REJECT:
+			if (ref->deletion)
+				print_ref_status('!', "[remote rejected]", ref, NULL, ref->error);
+			else
+				print_ref_status('!', "[remote rejected]", ref, ref->peer_ref, ref->error);
+			break;
 		case REF_STATUS_OK:
 			if (ref->deletion)
 				print_ref_status('-', "[deleted]", ref, NULL, NULL);
@@ -297,6 +323,7 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
 	int allow_deleting_refs = 0;
 	int expect_status_report = 0;
 	int flags = MATCH_REFS_NONE;
+	int ret;
 
 	if (args.send_all)
 		flags |= MATCH_REFS_ALL;
@@ -414,12 +441,15 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
 	}
 	close(out);
 
-	print_push_status(dest, remote_refs);
-
 	if (expect_status_report) {
-		if (receive_status(in))
+		ret = receive_status(in, remote_refs);
+		if (ret == -2)
 			return -1;
 	}
+	else
+		ret = 0;
+
+	print_push_status(dest, remote_refs);
 
 	if (!args.dry_run && remote) {
 		for (ref = remote_refs; ref; ref = ref->next)
@@ -428,6 +458,8 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
 
 	if (!new_refs)
 		fprintf(stderr, "Everything up-to-date\n");
+	if (ret < 0)
+		return ret;
 	for (ref = remote_refs; ref; ref = ref->next) {
 		switch (ref->status) {
 		case REF_STATUS_NONE:
diff --git a/cache.h b/cache.h
index ca5d96d..082e03b 100644
--- a/cache.h
+++ b/cache.h
@@ -509,7 +509,9 @@ struct ref {
 		REF_STATUS_NONFF,
 		REF_STATUS_NODELETE,
 		REF_STATUS_UPTODATE,
+		REF_STATUS_REMOTE_REJECT,
 	} status;
+	char *error;
 	struct ref *peer_ref; /* when renaming */
 	char name[FLEX_ARRAY]; /* more */
 };
-- 
1.5.3.5.1731.g0763

^ permalink raw reply related

* [PATCH v2 2/3] send-pack: check ref->status before updating tracking refs
From: Jeff King @ 2007-11-13 11:36 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Pierre Habouzit, Daniel Barkalow, Alex Riesen
In-Reply-To: <20071113102500.GA2767@sigill.intra.peff.net>

Previously, we manually checked the 'NONE' and 'UPTODATE'
conditions. Now that we have ref->status, we can easily
say "only update if we pushed successfully".

Signed-off-by: Jeff King <peff@peff.net>
---
 builtin-send-pack.c |   15 ++++-----------
 1 files changed, 4 insertions(+), 11 deletions(-)

diff --git a/builtin-send-pack.c b/builtin-send-pack.c
index c01a9c4..5c84766 100644
--- a/builtin-send-pack.c
+++ b/builtin-send-pack.c
@@ -180,24 +180,17 @@ static int receive_status(int in)
 static void update_tracking_ref(struct remote *remote, struct ref *ref)
 {
 	struct refspec rs;
-	int will_delete_ref;
 
-	rs.src = ref->name;
-	rs.dst = NULL;
-
-	if (!ref->peer_ref)
+	if (ref->status != REF_STATUS_OK)
 		return;
 
-	will_delete_ref = is_null_sha1(ref->peer_ref->new_sha1);
-
-	if (!will_delete_ref &&
-			!hashcmp(ref->old_sha1, ref->peer_ref->new_sha1))
-		return;
+	rs.src = ref->name;
+	rs.dst = NULL;
 
 	if (!remote_find_tracking(remote, &rs)) {
 		if (args.verbose)
 			fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst);
-		if (is_null_sha1(ref->peer_ref->new_sha1)) {
+		if (ref->deletion) {
 			if (delete_ref(rs.dst, NULL))
 				error("Failed to delete");
 		} else
-- 
1.5.3.5.1731.g0763

^ permalink raw reply related

* [PATCH v2 1/3] send-pack: track errors for each ref
From: Jeff King @ 2007-11-13 11:36 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Pierre Habouzit, Daniel Barkalow, Alex Riesen
In-Reply-To: <20071113102500.GA2767@sigill.intra.peff.net>

Instead of keeping the 'ret' variable, we instead have a
status flag for each ref that tracks what happened to it.
We then print the ref status after all of the refs have
been examined.

This paves the way for three improvements:
  - updating tracking refs only for non-error refs
  - incorporating remote rejection into the printed status
  - printing errors in a different order than we processed
    (e.g., consolidating non-ff errors near the end with
    a special message)

Signed-off-by: Jeff King <peff@peff.net>
---
 builtin-send-pack.c |  213 +++++++++++++++++++++++++++++----------------------
 cache.h             |   13 +++-
 2 files changed, 132 insertions(+), 94 deletions(-)

diff --git a/builtin-send-pack.c b/builtin-send-pack.c
index 418925e..c01a9c4 100644
--- a/builtin-send-pack.c
+++ b/builtin-send-pack.c
@@ -207,8 +207,9 @@ static void update_tracking_ref(struct remote *remote, struct ref *ref)
 	}
 }
 
-static const char *prettify_ref(const char *name)
+static const char *prettify_ref(const struct ref *ref)
 {
+	const char *name = ref->name;
 	return name + (
 		!prefixcmp(name, "refs/heads/") ? 11 :
 		!prefixcmp(name, "refs/tags/") ? 10 :
@@ -218,15 +219,90 @@ static const char *prettify_ref(const char *name)
 
 #define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
 
+static void print_ref_status(char flag, const char *summary, struct ref *to, struct ref *from, const char *msg)
+{
+	fprintf(stderr, " %c %-*s ", flag, SUMMARY_WIDTH, summary);
+	if (from)
+		fprintf(stderr, "%s -> %s", prettify_ref(from), prettify_ref(to));
+	else
+		fputs(prettify_ref(to), stderr);
+	if (msg) {
+		fputs(" (", stderr);
+		fputs(msg, stderr);
+		fputc(')', stderr);
+	}
+	fputc('\n', stderr);
+}
+
+
+static void print_push_status(const char *dest, struct ref *refs)
+{
+	struct ref *ref;
+	int shown_dest = 0;
+
+	for (ref = refs; ref; ref = ref->next) {
+		if (!ref->status)
+			continue;
+		if (ref->status == REF_STATUS_UPTODATE && !args.verbose)
+			continue;
+
+		if (!shown_dest) {
+			fprintf(stderr, "To %s\n", dest);
+			shown_dest = 1;
+		}
+
+		switch(ref->status) {
+		case REF_STATUS_NONE:
+			print_ref_status('X', "[no match]", ref, NULL, NULL);
+			break;
+		case REF_STATUS_NODELETE:
+			print_ref_status('!', "[rejected]", ref, NULL,
+					"remote does not support deleting refs");
+			break;
+		case REF_STATUS_UPTODATE:
+			print_ref_status('=', "[up to date]", ref, ref->peer_ref, NULL);
+			break;
+		case REF_STATUS_NONFF:
+			print_ref_status('!', "[rejected]", ref, ref->peer_ref, "non-fast forward");
+			break;
+		case REF_STATUS_OK:
+			if (ref->deletion)
+				print_ref_status('-', "[deleted]", ref, NULL, NULL);
+			else if (is_null_sha1(ref->old_sha1))
+				print_ref_status('*',
+						(prefixcmp(ref->name, "refs/tags/") ?
+						  "[new branch]" : "[new tag]"),
+						ref, ref->peer_ref, NULL);
+			else {
+				char quickref[83];
+				char type = ' ';
+				const char *msg = NULL;
+				const char *old_abb;
+
+				old_abb = find_unique_abbrev(ref->old_sha1, DEFAULT_ABBREV);
+				strcpy(quickref, old_abb ? old_abb : sha1_to_hex(ref->old_sha1));
+				if (ref->nonff) {
+					strcat(quickref, "...");
+					type = '+';
+					msg = " (forced update)";
+				}
+				else
+					strcat(quickref, "..");
+				strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));
+
+				print_ref_status(type, quickref, ref, ref->peer_ref, msg);
+			}
+		}
+	}
+}
+
 static int do_send_pack(int in, int out, struct remote *remote, const char *dest, int nr_refspec, const char **refspec)
 {
 	struct ref *ref;
 	int new_refs;
-	int ret = 0;
 	int ask_for_status_report = 0;
 	int allow_deleting_refs = 0;
 	int expect_status_report = 0;
-	int shown_dest = 0;
 	int flags = MATCH_REFS_NONE;
 
 	if (args.send_all)
@@ -262,10 +338,6 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
 	 */
 	new_refs = 0;
 	for (ref = remote_refs; ref; ref = ref->next) {
-		char old_hex[60], *new_hex;
-		int will_delete_ref;
-		const char *pretty_ref;
-		const char *pretty_peer = NULL; /* only used when not deleting */
 		const unsigned char *new_sha1;
 
 		if (!ref->peer_ref) {
@@ -276,29 +348,15 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
 		else
 			new_sha1 = ref->peer_ref->new_sha1;
 
-		if (!shown_dest) {
-			fprintf(stderr, "To %s\n", dest);
-			shown_dest = 1;
-		}
-
-		will_delete_ref = is_null_sha1(new_sha1);
-
-		pretty_ref = prettify_ref(ref->name);
-		if (!will_delete_ref)
-			pretty_peer = prettify_ref(ref->peer_ref->name);
 
-		if (will_delete_ref && !allow_deleting_refs) {
-			fprintf(stderr, " ! %-*s %s (remote does not support deleting refs)\n",
-					SUMMARY_WIDTH, "[rejected]", pretty_ref);
-			ret = -2;
+		ref->deletion = is_null_sha1(new_sha1);
+		if (ref->deletion && !allow_deleting_refs) {
+			ref->status = REF_STATUS_NODELETE;
 			continue;
 		}
-		if (!will_delete_ref &&
+		if (!ref->deletion &&
 		    !hashcmp(ref->old_sha1, new_sha1)) {
-			if (args.verbose)
-				fprintf(stderr, " = %-*s %s -> %s\n",
-					SUMMARY_WIDTH, "[up to date]",
-					pretty_peer, pretty_ref);
+			ref->status = REF_STATUS_UPTODATE;
 			continue;
 		}
 
@@ -321,33 +379,26 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
 		 *     always allowed.
 		 */
 
-		if (!args.force_update &&
-		    !will_delete_ref &&
+		ref->nonff =
+		    !ref->deletion &&
 		    !is_null_sha1(ref->old_sha1) &&
-		    !ref->force) {
-			if (!has_sha1_file(ref->old_sha1) ||
-			    !ref_newer(new_sha1, ref->old_sha1)) {
-				/* We do not have the remote ref, or
-				 * we know that the remote ref is not
-				 * an ancestor of what we are trying to
-				 * push.  Either way this can be losing
-				 * commits at the remote end and likely
-				 * we were not up to date to begin with.
-				 */
-				fprintf(stderr, " ! %-*s %s -> %s (non-fast forward)\n",
-						SUMMARY_WIDTH, "[rejected]",
-						pretty_peer, pretty_ref);
-				ret = -2;
-				continue;
-			}
+		    (!has_sha1_file(ref->old_sha1)
+		      || !ref_newer(new_sha1, ref->old_sha1));
+
+		if (ref->nonff && !ref->force && !args.force_update) {
+			ref->status = REF_STATUS_NONFF;
+			continue;
 		}
+
 		hashcpy(ref->new_sha1, new_sha1);
-		if (!will_delete_ref)
+		if (!ref->deletion)
 			new_refs++;
-		strcpy(old_hex, sha1_to_hex(ref->old_sha1));
-		new_hex = sha1_to_hex(ref->new_sha1);
+		ref->status = REF_STATUS_OK;
 
 		if (!args.dry_run) {
+			char *old_hex = sha1_to_hex(ref->old_sha1);
+			char *new_hex = sha1_to_hex(ref->new_sha1);
+
 			if (ask_for_status_report) {
 				packet_write(out, "%s %s %s%c%s",
 					old_hex, new_hex, ref->name, 0,
@@ -359,64 +410,42 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
 				packet_write(out, "%s %s %s",
 					old_hex, new_hex, ref->name);
 		}
-		if (will_delete_ref)
-			fprintf(stderr, " - %-*s %s\n",
-				SUMMARY_WIDTH, "[deleting]",
-				pretty_ref);
-		else if (is_null_sha1(ref->old_sha1)) {
-			const char *msg;
-
-			if (!prefixcmp(ref->name, "refs/tags/"))
-				msg = "[new tag]";
-			else
-				msg = "[new branch]";
-			fprintf(stderr, " * %-*s %s -> %s\n",
-				SUMMARY_WIDTH, msg,
-				pretty_peer, pretty_ref);
-		}
-		else {
-			char quickref[83];
-			char type = ' ';
-			const char *msg = "";
-			const char *old_abb;
-			old_abb = find_unique_abbrev(ref->old_sha1, DEFAULT_ABBREV);
-			strcpy(quickref, old_abb ? old_abb : old_hex);
-			if (ref_newer(ref->peer_ref->new_sha1, ref->old_sha1))
-				strcat(quickref, "..");
-			else {
-				strcat(quickref, "...");
-				type = '+';
-				msg = " (forced update)";
-			}
-			strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));
-
-			fprintf(stderr, " %c %-*s %s -> %s%s\n",
-				type,
-				SUMMARY_WIDTH, quickref,
-				pretty_peer, pretty_ref,
-				msg);
-		}
 	}
 
 	packet_flush(out);
-	if (new_refs && !args.dry_run)
-		ret = pack_objects(out, remote_refs);
+	if (new_refs && !args.dry_run) {
+		if (pack_objects(out, remote_refs) < 0) {
+			close(out);
+			return -1;
+		}
+	}
 	close(out);
 
+	print_push_status(dest, remote_refs);
+
 	if (expect_status_report) {
 		if (receive_status(in))
-			ret = -4;
+			return -1;
 	}
 
-	if (!args.dry_run && remote && ret == 0) {
+	if (!args.dry_run && remote) {
 		for (ref = remote_refs; ref; ref = ref->next)
-			if (!is_null_sha1(ref->new_sha1))
-				update_tracking_ref(remote, ref);
+			update_tracking_ref(remote, ref);
 	}
 
-	if (!new_refs && ret == 0)
+	if (!new_refs)
 		fprintf(stderr, "Everything up-to-date\n");
-	return ret;
+	for (ref = remote_refs; ref; ref = ref->next) {
+		switch (ref->status) {
+		case REF_STATUS_NONE:
+		case REF_STATUS_UPTODATE:
+		case REF_STATUS_OK:
+			break;
+		default:
+			return -1;
+		}
+	}
+	return 0;
 }
 
 static void verify_remote_names(int nr_heads, const char **heads)
diff --git a/cache.h b/cache.h
index 5f40e12..ca5d96d 100644
--- a/cache.h
+++ b/cache.h
@@ -499,8 +499,17 @@ struct ref {
 	struct ref *next;
 	unsigned char old_sha1[20];
 	unsigned char new_sha1[20];
-	unsigned char force;
-	unsigned char merge;
+	unsigned char force : 1;
+	unsigned char merge : 1;
+	unsigned char nonff : 1;
+	unsigned char deletion : 1;
+	enum {
+		REF_STATUS_NONE = 0,
+		REF_STATUS_OK,
+		REF_STATUS_NONFF,
+		REF_STATUS_NODELETE,
+		REF_STATUS_UPTODATE,
+	} status;
 	struct ref *peer_ref; /* when renaming */
 	char name[FLEX_ARRAY]; /* more */
 };
-- 
1.5.3.5.1731.g0763

^ permalink raw reply related

* Re: [RFC/PATCH 0/3] tracking per-ref errors on push
From: Jeff King @ 2007-11-13 11:34 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Pierre Habouzit, Daniel Barkalow, Alex Riesen
In-Reply-To: <20071113102500.GA2767@sigill.intra.peff.net>

On Tue, Nov 13, 2007 at 05:25:01AM -0500, Jeff King wrote:

>   - It looks like the push mirror code just made it into next, which
>     is going to require a conflict-heavy rebase on my part.

This turned out not to be too bad. A rebased series (with the extra
patch I just posted) follows, and it passes the test suite (though I
still think more tests are in order).

-Peff

^ permalink raw reply

* Re: [PATCH/RFC 1/3] send-pack: track errors for each ref
From: Jeff King @ 2007-11-13 11:32 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Pierre Habouzit, Daniel Barkalow, Alex Riesen
In-Reply-To: <20071113102709.GA2905@sigill.intra.peff.net>

On Tue, Nov 13, 2007 at 05:27:09AM -0500, Jeff King wrote:

> Instead of keeping the 'ret' variable, we instead have a
> status flag for each ref that tracks what happened to it.
> We then print the ref status after all of the refs have
> been examined.

Argh, this fails t5400 quite badly without the following patch:

---
diff --git a/builtin-send-pack.c b/builtin-send-pack.c
index 3ac2615..eff84e0 100644
--- a/builtin-send-pack.c
+++ b/builtin-send-pack.c
@@ -421,6 +421,16 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
 
 	if (!new_refs)
 		fprintf(stderr, "Everything up-to-date\n");
+	for (ref = remote_refs; ref; ref = ref->next) {
+		switch (ref->status) {
+		case REF_STATUS_NONE:
+		case REF_STATUS_UPTODATE:
+		case REF_STATUS_OK:
+			break;
+		default:
+			return -1;
+		}
+	}
 	return 0;
 }
 

^ permalink raw reply related

* Re: wishlist: git info
From: Thomas Neumann @ 2007-11-13 11:32 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git
In-Reply-To: <Pine.LNX.4.64.0711131111220.4362@racer.site>

> Is slightly troubles me that you put so much emphasis on what I would call 
> "remote information".  I understand that in svn, your working directory 
> without the server is not very useful.  But we do not have that problem.
that is true. My usage pattern probably stems from the fact that I am a
long term svn user :) And I use git for work now, where there is indeed
some kind of central repository just as in a Subversion setting.
In a fully decentralized setting the remote information is probably not
as important, although you might still want to know what happens if you
issue "git pull".


> FWIW I think a much better idea is to have that bash prompt that was 
> posted some months ago; there's not even a need to run a program manually 
> then!
a bash prompt is nice too, of course. But there is only so much
information you can reasonably encode in the prompt.
When you know the remote url (ok, this assumes a "centralized" model),
branch, head commit and date of the head commit (this is just for
humans), you know very precisely what you are looking at. For the more
decentralized users some other information might be relevant, I don't know.

While the head commit hash is enough to identify a point in the revision
history, the other information allows a human to identify the point in
the revision history easily. So I can see what is checked out, how old
the checkout is etc.

> His name is "Riesen", just like in the German translation of the famous 
> Newton statement.
sorry for the typo, I noticed it just the moment I had pressed send...
Sometimes I really wish I could edit mails after sending them.

Thomas

^ permalink raw reply

* Re: Cloning empty repositories, was Re: What is the idea for bare repositories?
From: Johannes Schindelin @ 2007-11-13 11:19 UTC (permalink / raw)
  To: Matthieu Moy; +Cc: Junio C Hamano, Bill Lear, Jan Wielemaker, git
In-Reply-To: <vpqzlxiiii6.fsf@bauges.imag.fr>

Hi,

On Tue, 13 Nov 2007, Matthieu Moy wrote:

> Junio C Hamano <gitster@pobox.com> writes:
> 
> > But both of Johannes's points apply equally well to an empty bare 
> > repository and to an empty non bare repository.  IOW, bareness does 
> > not matter to the suggestion Johannes gave.
> 
> He was suggesting to create the initial commit before cloning:
> 
> >> So you need to populate the repository before starting _anyway_.

Of course I was suggesting to push into the still empty, bare repository, 
before anybody is cloning.

Ciao,
Dscho

^ permalink raw reply

* Re: wishlist: git info
From: Johannes Schindelin @ 2007-11-13 11:13 UTC (permalink / raw)
  To: Thomas Neumann; +Cc: git
In-Reply-To: <fhbn50$uqu$1@ger.gmane.org>

Hi,

On Tue, 13 Nov 2007, Thomas Neumann wrote:

> Perhaps also project description (if it exists?) one can specify a 
> project description? I did not even know this. But yes, this would be 
> useful, too. In general I think git info should show everything to 
> quickly understand what is currently checked out.

Is slightly troubles me that you put so much emphasis on what I would call 
"remote information".  I understand that in svn, your working directory 
without the server is not very useful.  But we do not have that problem.

> The name of the current branch should probably be included, too.

FWIW I think a much better idea is to have that bash prompt that was 
posted some months ago; there's not even a need to run a program manually 
then!

> I use an alias with the commands proposed by Alex Riessen for now, but a 
> more general command would be nice.

His name is "Riesen", just like in the German translation of the famous 
Newton statement.

Ciao,
Dscho

^ permalink raw reply

* Re: Cloning empty repositories, was Re: What is the idea for bare repositories?
From: Matthieu Moy @ 2007-11-13 10:50 UTC (permalink / raw)
  To: Shawn O. Pearce
  Cc: Junio C Hamano, Johannes Schindelin, Bill Lear, Jan Wielemaker,
	git
In-Reply-To: <20071113100209.GE14735@spearce.org>

"Shawn O. Pearce" <spearce@spearce.org> writes:

> So setting up an empty tree is basically that:
>
> 	mkdir foo && cd foo && git init &&
> 	git remote add origin $url

It is not.

The "git remote add" thing adds this to my .git/config:

[remote "origin"]
        url = /tmp/git1
        fetch = +refs/heads/*:refs/remotes/origin/*

While clone normally does a bit more:

[remote "origin"]
        url = /tmp/git1/.git
        fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
        remote = origin
        merge = refs/heads/master

So, it's really

$ git remote add origin url
$ $EDITOR .git/config    # or perhaps I missed the way to set the two
                         # options easily.

I find it so conveinient to have it for non-empty clones, it's
frustrating to have to do it by hand for empty clones.

-- 
Matthieu

^ permalink raw reply

* Re: [PATCH/RFC 3/3] send-pack: assign remote errors to each ref
From: Jeff King @ 2007-11-13 10:29 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Pierre Habouzit, Daniel Barkalow, Alex Riesen
In-Reply-To: <20071113102900.GC2905@sigill.intra.peff.net>

On Tue, Nov 13, 2007 at 05:29:00AM -0500, Jeff King wrote:

> +		ref->error = xstrdup(msg);

Another problem with this patch: this is a leak.

-Peff

^ permalink raw reply

* [PATCH/RFC 3/3] send-pack: assign remote errors to each ref
From: Jeff King @ 2007-11-13 10:29 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Pierre Habouzit, Daniel Barkalow, Alex Riesen
In-Reply-To: <20071113102500.GA2767@sigill.intra.peff.net>

This lets us show remote errors (e.g., a denied hook) along
with the usual push output. There are two drawbacks to this
change:

  1. cross-referencing the incoming status with the ref list
     is worst case O(n^2) (where n = number of refs); this
     can be fixed with a smarter implementation

  2. the status parsing is not foolproof. We get a line like

         ng refs/heads/master arbitrary msg

     which cannot be parsed unambiguously in the face of
     refnames with spaces. We do a prefix-match so that
     you will only run into problems if you have two refs,
     one of which is a prefix match of the other, and the
     longer having a space right after the prefix.
---
 builtin-send-pack.c |   44 +++++++++++++++++++++++++++++++++++++-------
 cache.h             |    2 ++
 2 files changed, 39 insertions(+), 7 deletions(-)

diff --git a/builtin-send-pack.c b/builtin-send-pack.c
index 2805c92..a2307fa 100644
--- a/builtin-send-pack.c
+++ b/builtin-send-pack.c
@@ -146,14 +146,34 @@ static void get_local_heads(void)
 	for_each_ref(one_local_ref, NULL);
 }
 
-static int receive_status(int in)
+static void set_ref_error(struct ref *refs, const char *line) {
+	struct ref *ref;
+
+	for (ref = refs; ref; ref = ref->next) {
+		const char *msg;
+		if (prefixcmp(line, ref->name))
+			continue;
+		msg = line + strlen(ref->name);
+		if (*msg++ != ' ')
+			continue;
+		ref->status = REF_STATUS_REMOTE_REJECT;
+		ref->error = xstrdup(msg);
+		ref->error[strlen(ref->error)-1] = '\0';
+		return;
+	}
+}
+
+/* a return value of -1 indicates that an error occurred,
+ * but we were able to set individual ref errors. A return
+ * value of -2 means we couldn't even get that far. */
+static int receive_status(int in, struct ref *refs)
 {
 	char line[1000];
 	int ret = 0;
 	int len = packet_read_line(in, line, sizeof(line));
 	if (len < 10 || memcmp(line, "unpack ", 7)) {
 		fprintf(stderr, "did not receive status back\n");
-		return -1;
+		return -2;
 	}
 	if (memcmp(line, "unpack ok\n", 10)) {
 		fputs(line, stderr);
@@ -171,7 +191,7 @@ static int receive_status(int in)
 		}
 		if (!memcmp(line, "ok", 2))
 			continue;
-		fputs(line, stderr);
+		set_ref_error(refs, line + 3);
 		ret = -1;
 	}
 	return ret;
@@ -258,6 +278,12 @@ static void print_push_status(const char *dest, struct ref *refs)
 		case REF_STATUS_NONFF:
 			print_ref_status('!', "[rejected]", ref, ref->peer_ref, "non-fast forward");
 			break;
+		case REF_STATUS_REMOTE_REJECT:
+			if (ref->deletion)
+				print_ref_status('!', "[remote rejected]", ref, NULL, ref->error);
+			else
+				print_ref_status('!', "[remote rejected]", ref, ref->peer_ref, ref->error);
+			break;
 		case REF_STATUS_OK:
 			if (ref->deletion)
 				print_ref_status('-', "[deleted]", ref, NULL, NULL);
@@ -296,6 +322,7 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
 	int ask_for_status_report = 0;
 	int allow_deleting_refs = 0;
 	int expect_status_report = 0;
+	int ret;
 
 	/* No funny business with the matcher */
 	remote_tail = get_remote_heads(in, &remote_refs, 0, NULL, REF_NORMAL);
@@ -400,12 +427,15 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
 	}
 	close(out);
 
-	print_push_status(dest, remote_refs);
-
 	if (expect_status_report) {
-		if (receive_status(in))
+		ret = receive_status(in, remote_refs);
+		if (ret == -2)
 			return -1;
 	}
+	else
+		ret = 0;
+
+	print_push_status(dest, remote_refs);
 
 	if (!args.dry_run && remote) {
 		for (ref = remote_refs; ref; ref = ref->next)
@@ -414,7 +444,7 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
 
 	if (!new_refs)
 		fprintf(stderr, "Everything up-to-date\n");
-	return 0;
+	return ret;
 }
 
 static void verify_remote_names(int nr_heads, const char **heads)
diff --git a/cache.h b/cache.h
index ca5d96d..082e03b 100644
--- a/cache.h
+++ b/cache.h
@@ -509,7 +509,9 @@ struct ref {
 		REF_STATUS_NONFF,
 		REF_STATUS_NODELETE,
 		REF_STATUS_UPTODATE,
+		REF_STATUS_REMOTE_REJECT,
 	} status;
+	char *error;
 	struct ref *peer_ref; /* when renaming */
 	char name[FLEX_ARRAY]; /* more */
 };
-- 
1.5.3.5.1704.g24d42-dirty

^ permalink raw reply related

* [PATCH/RFC 2/3] send-pack: check ref->status before updating tracking refs
From: Jeff King @ 2007-11-13 10:27 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Pierre Habouzit, Daniel Barkalow, Alex Riesen
In-Reply-To: <20071113102500.GA2767@sigill.intra.peff.net>

Previously, we manually checked the 'NONE' and 'UPTODATE'
conditions. Now that we have ref->status, we can easily
say "only update if we pushed successfully".
---
 builtin-send-pack.c |   15 ++++-----------
 1 files changed, 4 insertions(+), 11 deletions(-)

diff --git a/builtin-send-pack.c b/builtin-send-pack.c
index 3ac2615..2805c92 100644
--- a/builtin-send-pack.c
+++ b/builtin-send-pack.c
@@ -180,24 +180,17 @@ static int receive_status(int in)
 static void update_tracking_ref(struct remote *remote, struct ref *ref)
 {
 	struct refspec rs;
-	int will_delete_ref;
 
-	rs.src = ref->name;
-	rs.dst = NULL;
-
-	if (!ref->peer_ref)
+	if (ref->status != REF_STATUS_OK)
 		return;
 
-	will_delete_ref = is_null_sha1(ref->peer_ref->new_sha1);
-
-	if (!will_delete_ref &&
-			!hashcmp(ref->old_sha1, ref->peer_ref->new_sha1))
-		return;
+	rs.src = ref->name;
+	rs.dst = NULL;
 
 	if (!remote_find_tracking(remote, &rs)) {
 		if (args.verbose)
 			fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst);
-		if (is_null_sha1(ref->peer_ref->new_sha1)) {
+		if (ref->deletion) {
 			if (delete_ref(rs.dst, NULL))
 				error("Failed to delete");
 		} else
-- 
1.5.3.5.1704.g24d42-dirty

^ permalink raw reply related

* [PATCH/RFC 1/3] send-pack: track errors for each ref
From: Jeff King @ 2007-11-13 10:27 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Pierre Habouzit, Daniel Barkalow, Alex Riesen
In-Reply-To: <20071113102500.GA2767@sigill.intra.peff.net>

Instead of keeping the 'ret' variable, we instead have a
status flag for each ref that tracks what happened to it.
We then print the ref status after all of the refs have
been examined.

This paves the way for three improvements:
  - updating tracking refs only for non-error refs
  - incorporating remote rejection into the printed status
  - printing errors in a different order than we processed
    (e.g., consolidating non-ff errors near the end with
    a special message)
---
 builtin-send-pack.c |  201 ++++++++++++++++++++++++++++-----------------------
 cache.h             |   13 +++-
 2 files changed, 121 insertions(+), 93 deletions(-)

diff --git a/builtin-send-pack.c b/builtin-send-pack.c
index 5a0f5c6..3ac2615 100644
--- a/builtin-send-pack.c
+++ b/builtin-send-pack.c
@@ -207,8 +207,9 @@ static void update_tracking_ref(struct remote *remote, struct ref *ref)
 	}
 }
 
-static const char *prettify_ref(const char *name)
+static const char *prettify_ref(const struct ref *ref)
 {
+	const char *name = ref->name;
 	return name + (
 		!prefixcmp(name, "refs/heads/") ? 11 :
 		!prefixcmp(name, "refs/tags/") ? 10 :
@@ -218,15 +219,90 @@ static const char *prettify_ref(const char *name)
 
 #define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
 
+static void print_ref_status(char flag, const char *summary, struct ref *to, struct ref *from, const char *msg)
+{
+	fprintf(stderr, " %c %-*s ", flag, SUMMARY_WIDTH, summary);
+	if (from)
+		fprintf(stderr, "%s -> %s", prettify_ref(from), prettify_ref(to));
+	else
+		fputs(prettify_ref(to), stderr);
+	if (msg) {
+		fputs(" (", stderr);
+		fputs(msg, stderr);
+		fputc(')', stderr);
+	}
+	fputc('\n', stderr);
+}
+
+
+static void print_push_status(const char *dest, struct ref *refs)
+{
+	struct ref *ref;
+	int shown_dest = 0;
+
+	for (ref = refs; ref; ref = ref->next) {
+		if (!ref->status)
+			continue;
+		if (ref->status == REF_STATUS_UPTODATE && !args.verbose)
+			continue;
+
+		if (!shown_dest) {
+			fprintf(stderr, "To %s\n", dest);
+			shown_dest = 1;
+		}
+
+		switch(ref->status) {
+		case REF_STATUS_NONE:
+			print_ref_status('X', "[no match]", ref, NULL, NULL);
+			break;
+		case REF_STATUS_NODELETE:
+			print_ref_status('!', "[rejected]", ref, NULL,
+					"remote does not support deleting refs");
+			break;
+		case REF_STATUS_UPTODATE:
+			print_ref_status('=', "[up to date]", ref, ref->peer_ref, NULL);
+			break;
+		case REF_STATUS_NONFF:
+			print_ref_status('!', "[rejected]", ref, ref->peer_ref, "non-fast forward");
+			break;
+		case REF_STATUS_OK:
+			if (ref->deletion)
+				print_ref_status('-', "[deleted]", ref, NULL, NULL);
+			else if (is_null_sha1(ref->old_sha1))
+				print_ref_status('*',
+						(prefixcmp(ref->name, "refs/tags/") ?
+						  "[new branch]" : "[new tag]"),
+						ref, ref->peer_ref, NULL);
+			else {
+				char quickref[83];
+				char type = ' ';
+				const char *msg = NULL;
+				const char *old_abb;
+
+				old_abb = find_unique_abbrev(ref->old_sha1, DEFAULT_ABBREV);
+				strcpy(quickref, old_abb ? old_abb : sha1_to_hex(ref->old_sha1));
+				if (ref->nonff) {
+					strcat(quickref, "...");
+					type = '+';
+					msg = " (forced update)";
+				}
+				else
+					strcat(quickref, "..");
+				strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));
+
+				print_ref_status(type, quickref, ref, ref->peer_ref, msg);
+			}
+		}
+	}
+}
+
 static int do_send_pack(int in, int out, struct remote *remote, const char *dest, int nr_refspec, const char **refspec)
 {
 	struct ref *ref;
 	int new_refs;
-	int ret = 0;
 	int ask_for_status_report = 0;
 	int allow_deleting_refs = 0;
 	int expect_status_report = 0;
-	int shown_dest = 0;
 
 	/* No funny business with the matcher */
 	remote_tail = get_remote_heads(in, &remote_refs, 0, NULL, REF_NORMAL);
@@ -256,35 +332,17 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
 	 */
 	new_refs = 0;
 	for (ref = remote_refs; ref; ref = ref->next) {
-		char old_hex[60], *new_hex;
-		int will_delete_ref;
-		const char *pretty_ref;
-		const char *pretty_peer;
-
 		if (!ref->peer_ref)
 			continue;
 
-		if (!shown_dest) {
-			fprintf(stderr, "To %s\n", dest);
-			shown_dest = 1;
-		}
-
-		pretty_ref = prettify_ref(ref->name);
-		pretty_peer = prettify_ref(ref->peer_ref->name);
-
-		will_delete_ref = is_null_sha1(ref->peer_ref->new_sha1);
-		if (will_delete_ref && !allow_deleting_refs) {
-			fprintf(stderr, " ! %-*s %s (remote does not support deleting refs)\n",
-					SUMMARY_WIDTH, "[rejected]", pretty_ref);
-			ret = -2;
+		ref->deletion = is_null_sha1(ref->peer_ref->new_sha1);
+		if (ref->deletion && !allow_deleting_refs) {
+			ref->status = REF_STATUS_NODELETE;
 			continue;
 		}
-		if (!will_delete_ref &&
+		if (!ref->deletion &&
 		    !hashcmp(ref->old_sha1, ref->peer_ref->new_sha1)) {
-			if (args.verbose)
-				fprintf(stderr, " = %-*s %s -> %s\n",
-					SUMMARY_WIDTH, "[up to date]",
-					pretty_peer, pretty_ref);
+			ref->status = REF_STATUS_UPTODATE;
 			continue;
 		}
 
@@ -307,34 +365,26 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
 		 *     always allowed.
 		 */
 
-		if (!args.force_update &&
-		    !will_delete_ref &&
+		ref->nonff =
+		    !ref->deletion &&
 		    !is_null_sha1(ref->old_sha1) &&
-		    !ref->force) {
-			if (!has_sha1_file(ref->old_sha1) ||
-			    !ref_newer(ref->peer_ref->new_sha1,
-				       ref->old_sha1)) {
-				/* We do not have the remote ref, or
-				 * we know that the remote ref is not
-				 * an ancestor of what we are trying to
-				 * push.  Either way this can be losing
-				 * commits at the remote end and likely
-				 * we were not up to date to begin with.
-				 */
-				fprintf(stderr, " ! %-*s %s -> %s (non-fast forward)\n",
-						SUMMARY_WIDTH, "[rejected]",
-						pretty_peer, pretty_ref);
-				ret = -2;
-				continue;
-			}
+		    (!has_sha1_file(ref->old_sha1)
+		      || !ref_newer(ref->peer_ref->new_sha1, ref->old_sha1));
+
+		if (ref->nonff && !ref->force && !args.force_update) {
+			ref->status = REF_STATUS_NONFF;
+			continue;
 		}
+
 		hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
-		if (!will_delete_ref)
+		if (!ref->deletion)
 			new_refs++;
-		strcpy(old_hex, sha1_to_hex(ref->old_sha1));
-		new_hex = sha1_to_hex(ref->new_sha1);
+		ref->status = REF_STATUS_OK;
 
 		if (!args.dry_run) {
+			char *old_hex = sha1_to_hex(ref->old_sha1);
+			char *new_hex = sha1_to_hex(ref->new_sha1);
+
 			if (ask_for_status_report) {
 				packet_write(out, "%s %s %s%c%s",
 					old_hex, new_hex, ref->name, 0,
@@ -346,63 +396,32 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
 				packet_write(out, "%s %s %s",
 					old_hex, new_hex, ref->name);
 		}
-		if (will_delete_ref)
-			fprintf(stderr, " - %-*s %s\n",
-				SUMMARY_WIDTH, "[deleting]",
-				pretty_ref);
-		else if (is_null_sha1(ref->old_sha1)) {
-			const char *msg;
-
-			if (!prefixcmp(ref->name, "refs/tags/"))
-				msg = "[new tag]";
-			else
-				msg = "[new branch]";
-			fprintf(stderr, " * %-*s %s -> %s\n",
-				SUMMARY_WIDTH, msg,
-				pretty_peer, pretty_ref);
-		}
-		else {
-			char quickref[83];
-			char type = ' ';
-			const char *msg = "";
-			const char *old_abb;
-			old_abb = find_unique_abbrev(ref->old_sha1, DEFAULT_ABBREV);
-			strcpy(quickref, old_abb ? old_abb : old_hex);
-			if (ref_newer(ref->peer_ref->new_sha1, ref->old_sha1))
-				strcat(quickref, "..");
-			else {
-				strcat(quickref, "...");
-				type = '+';
-				msg = " (forced update)";
-			}
-			strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));
-
-			fprintf(stderr, " %c %-*s %s -> %s%s\n",
-				type,
-				SUMMARY_WIDTH, quickref,
-				pretty_peer, pretty_ref,
-				msg);
-		}
 	}
 
 	packet_flush(out);
-	if (new_refs && !args.dry_run)
-		ret = pack_objects(out, remote_refs);
+	if (new_refs && !args.dry_run) {
+		if (pack_objects(out, remote_refs) < 0) {
+			close(out);
+			return -1;
+		}
+	}
 	close(out);
 
+	print_push_status(dest, remote_refs);
+
 	if (expect_status_report) {
 		if (receive_status(in))
-			ret = -4;
+			return -1;
 	}
 
-	if (!args.dry_run && remote && ret == 0) {
+	if (!args.dry_run && remote) {
 		for (ref = remote_refs; ref; ref = ref->next)
 			update_tracking_ref(remote, ref);
 	}
 
-	if (!new_refs && ret == 0)
+	if (!new_refs)
 		fprintf(stderr, "Everything up-to-date\n");
-	return ret;
+	return 0;
 }
 
 static void verify_remote_names(int nr_heads, const char **heads)
diff --git a/cache.h b/cache.h
index 5f40e12..ca5d96d 100644
--- a/cache.h
+++ b/cache.h
@@ -499,8 +499,17 @@ struct ref {
 	struct ref *next;
 	unsigned char old_sha1[20];
 	unsigned char new_sha1[20];
-	unsigned char force;
-	unsigned char merge;
+	unsigned char force : 1;
+	unsigned char merge : 1;
+	unsigned char nonff : 1;
+	unsigned char deletion : 1;
+	enum {
+		REF_STATUS_NONE = 0,
+		REF_STATUS_OK,
+		REF_STATUS_NONFF,
+		REF_STATUS_NODELETE,
+		REF_STATUS_UPTODATE,
+	} status;
 	struct ref *peer_ref; /* when renaming */
 	char name[FLEX_ARRAY]; /* more */
 };
-- 
1.5.3.5.1704.g24d42-dirty

^ permalink raw reply related

* [RFC/PATCH 0/3] tracking per-ref errors on push
From: Jeff King @ 2007-11-13 10:25 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Pierre Habouzit, Daniel Barkalow, Alex Riesen

Following is a series to track push errors for individual refs.  This
should have three immediate advantages (the first two of which I
implement in the series):

  - updating tracking refs only for non-error refs
  - incorporating remote rejection into the printed status
  - printing errors in a different order than we processed
    (e.g., consolidating non-ff errors near the end with
    a special message)

The patches are:

  1. send-pack: track errors for each ref
     This is the groundwork for the other 2. I also think it makes the
     code a bit more readable by splitting out the print formatting from
     the push logic, and by explicitly naming the different states that
     we were previously deducing from the states of other variables.

  2. send-pack: check ref->status before updating tracking refs
     This should fix the bugs that people are seeing from 334f483

  3. send-pack: assign remote errors to each ref
     This may have some problems, as I will explain below.

I have a few concerns which make this an RFC and not a real patch
submission:

  - I have done pretty minimal testing, and there are no automated tests
    (at least 2, which fixes a real bug, should get a test).
    Unfortunately, I am out of time to work on this and am leaving the
    country for a week. Between that and Thanksgiving travel, I'm not
    sure when I'll have time to finish up, so I thought it best to have
    comments waiting (and others should feel free to pick up the work if
    they want -- Alex, I think your error consolidation should go nicely
    on top of this).

  - It looks like the push mirror code just made it into next, which
    is going to require a conflict-heavy rebase on my part.

  - There is a potential performance bottleneck in patch 3. See the
    commit message.

  - In patch 3, the 'ng' message sent by the remote are not
    unambiguously parseable.  See the commit message.

-Peff

^ permalink raw reply

* Re: Cloning empty repositories, was Re: What is the idea for bare repositories?
From: Jakub Narebski @ 2007-11-13 10:08 UTC (permalink / raw)
  To: git
In-Reply-To: <vpqzlxiiii6.fsf@bauges.imag.fr>

Matthieu Moy wrote:

> I repeat the use-case I mentionned above :
> 
> ,----
> | a typical use-case is when I want to create a new project. I'd
> | like to initialize an empty bare repo on my backed up disk, and then
> | clone it to my local-fast-unreliable disk to get a working copy and do
> | the first commit there.
> `----
> 
> I find this quite natural, and up to now, no one gave me either a
> rationale not to do that,

The rationale is that current git just simply cannot do this.
You are welcome to add support for this corner case in git-clone,
or add git protocol extension for symref transfer.

-- 
Jakub Narebski
Warsaw, Poland
ShadeHawk on #git

^ 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