# Re: Stupid Git question

7 messages from 2006-11-21 to 2006-11-24. Participants: Carl Worth, Junio C Hamano, Sean Kelley, Jakub Narebski.
Thread: https://gitlist.dev/t/43146

## Sean Kelley, 2006-11-21 21:41

Subject: Stupid Git question
Message-ID: <89b129c60611211341j71079633g53b0ec1d2e3193a5@mail.gmail.com>
URL: https://gitlist.dev/e/89b129c60611211341j71079633g53b0ec1d2e3193a5%40mail.gmail.com
In-Reply-To: <89b129c60611211331r3bb286b6re3c2c8f65ec3896f@mail.gmail.com>

```
Hi,

I have a stupid git question.  We are doing embedded development using
git for our kernel mods.

git clone git+ssh://git.example.com/git/kernel/mh.git kernel

git checkout -b fm-modulator

edit/add/commit

git checkout origin

git pull . fm-modulator

git push origin

Everything up-to-date  <<< It pushes nothing

My problem is that I don't understand why when I tell git to push the
changes to our repository it says everything is up-to-date.  It
clearly hasn't pushed it yet to our server.

My git layout is like this:

A single repository representing our Monahans kernel "mh.git"  hosted
on a remote server accessed by git+ssh.

Four developers work on the kernel and drivers for the target platform.

Any suggestions much appreciated.  My prior experience is with
StarTeam and more recently Subversion.

Thanks,

Sean

-- 
Sean Kelley

-- 

```

## Jakub Narebski, 2006-11-21 21:49

Subject: Re: Stupid Git question
Message-ID: <ejvs65$vo8$1@sea.gmane.org>
URL: https://gitlist.dev/e/ejvs65%24vo8%241%40sea.gmane.org
In-Reply-To: <89b129c60611211341j71079633g53b0ec1d2e3193a5@mail.gmail.com>

```
Sean Kelley wrote:

> git checkout origin

It should be "git checkout master". You shouldn't do work on tracking
branches like origin branch.

> git pull . fm-modulator
> 
> git push origin

Here origin means origin remote (repository). Check out what you have in
remotes/origin, or in [remote "origin"] section in git config.
-- 
Jakub Narebski
Warsaw, Poland
ShadeHawk on #git


```

## Sean Kelley, 2006-11-22 14:28

Subject: Re: Stupid Git question
Message-ID: <89b129c60611220628l59e305b8h4d2196f7cf6498d4@mail.gmail.com>
URL: https://gitlist.dev/e/89b129c60611220628l59e305b8h4d2196f7cf6498d4%40mail.gmail.com
In-Reply-To: <ejvs65$vo8$1@sea.gmane.org>

```
On 11/21/06, Jakub Narebski <jnareb@gmail.com> wrote:
> Sean Kelley wrote:
>
> > git checkout origin
>
> It should be "git checkout master". You shouldn't do work on tracking
> branches like origin branch.
>
> > git pull . fm-modulator
> >
> > git push origin
>
> Here origin means origin remote (repository). Check out what you have in
> remotes/origin, or in [remote "origin"] section in git config.


Thanks!  One more question.  It appears that the problem that I am
having is that people are comitting to origin and should be committing
to master.  Perhaps the names can be confusing.  One suggestion made
is that we give a branch on the remote server a more meaningful name.

If on my remote server I have:

  /data/git/kernel/mh.git

How do I add a branch to the remote repository that is visible to all
team members.  It seems like the git checkout -b commands just create
local topic branches.

So I would have something like:

git clone git+ssh://git.example.com/data/git/kernel/mh.git kernel
cd kernel
git checkout Project
git checkout -b fm-modulator
edit/add/commit changes...

git checkout Project
git pull . fm-modulator
git push origin Project

So how do I create this Project branch on the remote repository such
that it is visible to all?  Do I log onto the remove server and do it
manually?  If so, how is that done?

Thanks!

Sean

> --
> Jakub Narebski
> Warsaw, Poland
> ShadeHawk on #git
>
>
> -
> 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
>


-- 

```

## Carl Worth, 2006-11-22 16:44

Subject: Re: Stupid Git question
Message-ID: <871wnvxwg4.wl%cworth@cworth.org>
URL: https://gitlist.dev/e/871wnvxwg4.wl%25cworth%40cworth.org
In-Reply-To: <89b129c60611220628l59e305b8h4d2196f7cf6498d4@mail.gmail.com>

```
On Wed, 22 Nov 2006 08:28:58 -0600, "Sean Kelley" wrote:
> How do I add a branch to the remote repository that is visible to all
> team members.  It seems like the git checkout -b commands just create
> local topic branches.

Just push the branch out to the remote repository. You even gave the
command sequence to do that:

> git checkout Project
> git pull . fm-modulator
> git push origin Project

-Carl

```

## Sean Kelley, 2006-11-22 21:28

Subject: Re: Stupid Git question
Message-ID: <89b129c60611221328l333d22c6o3668aef2706f92c7@mail.gmail.com>
URL: https://gitlist.dev/e/89b129c60611221328l333d22c6o3668aef2706f92c7%40mail.gmail.com
In-Reply-To: <871wnvxwg4.wl%cworth@cworth.org>

```
Hi,

On 11/22/06, Carl Worth <cworth@cworth.org> wrote:
> On Wed, 22 Nov 2006 08:28:58 -0600, "Sean Kelley" wrote:
> > How do I add a branch to the remote repository that is visible to all
> > team members.  It seems like the git checkout -b commands just create
> > local topic branches.
>
> Just push the branch out to the remote repository. You even gave the
> command sequence to do that:
>
> > git checkout Project
> > git pull . fm-modulator
> > git push origin Project
>

One other question - how do you rename a branch on the remote
repository once you have created it?

Thanks,

Sean

> -Carl
>
>
>


-- 

```

## Junio C Hamano, 2006-11-22 22:43

Subject: Re: Stupid Git question
Message-ID: <7vac2jp0g6.fsf@assigned-by-dhcp.cox.net>
URL: https://gitlist.dev/e/7vac2jp0g6.fsf%40assigned-by-dhcp.cox.net
In-Reply-To: <89b129c60611221328l333d22c6o3668aef2706f92c7@mail.gmail.com>

```
"Sean Kelley" <sean.v.kelley@gmail.com> writes:

> One other question - how do you rename a branch on the remote
> repository once you have created it?

Right now, there is no way to remove a ref, so even "create new
and then remove" would not work.  You need a way to ssh-in to
the machine and run "branch -d" there.

You would need an access to run git tools on the remote site
for:

 - repository creation and deletion
 - ref deletion
 - fsck, pruning and repacking
 - adding entries to objects/info/alternates
 - managing hook scripts

with the current set of tools, which pretty much means an
account with a full shell access over SSH.

Earlier in another thread, Linus said that it is justifiable to
treat repository creation as a special event and outside of
git.  For one thing you need to have the account on the site and
arrange access permissions and authentication before you can
create a repository, so it is an understandable position to
take, and for people with full SSH access it is a minor nuisance
that they have to first go there to perform the above operations
instead of running "git-do-things-at-remote host:path" locally.

However, for sites that want to restrict the access via
git-shell, after a repository owner secured such an account and
access rights, not being able to allow the user to do some of
the above things himself is a burden on site administrators.
This _could_ be improved by allowing some common operations via
git-shell.

Even under git-shell, the process'es user and group
credentials are the primary means to control the access
rights.  So in that sense, letting the user to say things
like the following might make sense:

   $ REPO=repo.example.com:/pub/scm/git/project.git
   $ git remote-admin $REPO create-repository
   $ git remote-admin $REPO delete-repository
   $ git remote-admin $REPO repack
   $ git remote-admin $REPO fsck-objects
   $ git remote-admin $REPO count-objects

And for the sake of both simplicity (which would lead to
security) and to allow the site administrator to make policy
decision, I think we do not have to (and we shouldn't) make the
above commands to take any flags.  The command's availability
and what parameters to be passed to underlying commands such as
git-repack are determined by the site administrator.  For
example, an administrator may give a restricted account to a
user _and_ set up one repository for him but may not want to
give him rights to create another repository nor delete that
initial repository given to him, in which case create-repository
and delete-repository actions would be disabled.

I have a feeling that the users should not be given full control
over 'hook' scripts, but I am not sure.  A site administator
might want to forbid too expensive hooks from running, even the
process spawned by the user would work only in directories that
the user has access to.  If we give the users a full control,
then:

   $ git remote-admin $REPO get-hook $hookname >old-contents
   $ git remote-admin $REPO put-hook $hookname <new-contents
   $ git remote-admin $REPO remove-hook $hookname

would be the set of commands we could use (I am assuming
put-hook installs the hook in "enabled" state, and get-hook
would give a failure for nonexistent or disabled hooks).

The most straightforward extension of the above for ref deletion
is to say:

   $ git remote-admin $REPO delete-refs refs/heads/foo refs/tags/v1.0

and that would be the simplest way to implement it if we were to
go with "git remote-admin".  However, I think people would find
it more natural if manipulation of refs were part of "git push".

"git push $REPO $src:$dst" means "take what I have in $src in my
local repository, and update the $REPO's $dst ref with that".
So as a natural extension of that, we could make:

   $ git push $REPO '':$dst

to mean "store nothingness in $dst" and make that a way to
express the desire to remove $dst ref.


```

## Junio C Hamano, 2006-11-24 08:31

Subject: Re: Stupid Git question
Message-ID: <7v3b89dz5i.fsf@assigned-by-dhcp.cox.net>
URL: https://gitlist.dev/e/7v3b89dz5i.fsf%40assigned-by-dhcp.cox.net
In-Reply-To: <7vac2jp0g6.fsf@assigned-by-dhcp.cox.net>

```
Junio C Hamano <junkio@cox.net> writes:

>...
> The most straightforward extension of the above for ref deletion
> is to say:
>
>    $ git remote-admin $REPO delete-refs refs/heads/foo refs/tags/v1.0
>
> and that would be the simplest way to implement it if we were to
> go with "git remote-admin".  However, I think people would find
> it more natural if manipulation of refs were part of "git push".
>
> "git push $REPO $src:$dst" means "take what I have in $src in my
> local repository, and update the $REPO's $dst ref with that".
> So as a natural extension of that, we could make:
>
>    $ git push $REPO '':$dst
>
> to mean "store nothingness in $dst" and make that a way to
> express the desire to remove $dst ref.

And here is an attempt to do so.  Only lightly tested...

	Whenever I say "only lightly tested", I am hoping that
	interested people on the list to test it and possibly
	enhance it with follow-up patches.  Or at least respond
	with "Hey, that sucks" or "Ok, it seems to work for your
	test case but here is a breakage".

-- >8 --
[PATCH] Allow git push to delete remote ref.

This allows you to say

	git send-pack $URL :refs/heads/$branch

to delete the named remote branch.  The refspec $src:$dst means
replace the destination ref with the object known as $src on the
local side, so this is a natural extension to make an empty $src
mean "No object" to delete the target.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 connect.c            |   11 ++++++++++-
 receive-pack.c       |   45 ++++++++++++++++++++++++++++++++++-----------
 send-pack.c          |   41 ++++++++++++++++++++++++++++++-----------
 t/t5400-send-pack.sh |   10 ++++++++++
 4 files changed, 84 insertions(+), 23 deletions(-)

diff --git a/connect.c b/connect.c
index b9666cc..f7edba8 100644
--- a/connect.c
+++ b/connect.c
@@ -144,6 +144,7 @@ struct refspec {
  * +A:B means overwrite remote B with local A.
  * +A is a shorthand for +A:A.
  * A is a shorthand for A:A.
+ * :B means delete remote B.
  */
 static struct refspec *parse_ref_spec(int nr_refspec, char **refspec)
 {
@@ -240,6 +241,13 @@ static struct ref *try_explicit_object_n
 	unsigned char sha1[20];
 	struct ref *ref;
 	int len;
+
+	if (!*name) {
+		ref = xcalloc(1, sizeof(*ref) + 20);
+		strcpy(ref->name, "(delete)");
+		hashclr(ref->new_sha1);
+		return ref;
+	}
 	if (get_sha1(name, sha1))
 		return NULL;
 	len = strlen(name) + 1;
@@ -262,7 +270,8 @@ static int match_explicit_refs(struct re
 			break;
 		case 0:
 			/* The source could be in the get_sha1() format
-			 * not a reference name.
+			 * not a reference name.  :refs/other is a
+			 * way to delete 'other' ref at the remote end.
 			 */
 			matched_src = try_explicit_object_name(rs[i].src);
 			if (matched_src)
diff --git a/receive-pack.c b/receive-pack.c
index d56898c..1a141dc 100644
--- a/receive-pack.c
+++ b/receive-pack.c
@@ -14,7 +14,7 @@ static int deny_non_fast_forwards = 0;
 static int unpack_limit = 5000;
 static int report_status;
 
-static char capabilities[] = "report-status";
+static char capabilities[] = " report-status delete-refs ";
 static int capabilities_sent;
 
 static int receive_pack_config(const char *var, const char *value)
@@ -113,12 +113,14 @@ static int update(struct command *cmd)
 
 	strcpy(new_hex, sha1_to_hex(new_sha1));
 	strcpy(old_hex, sha1_to_hex(old_sha1));
-	if (!has_sha1_file(new_sha1)) {
+
+	if (!is_null_sha1(new_sha1) && !has_sha1_file(new_sha1)) {
 		cmd->error_string = "bad pack";
 		return error("unpack should have generated %s, "
 			     "but I can't find it!", new_hex);
 	}
-	if (deny_non_fast_forwards && !is_null_sha1(old_sha1)) {
+	if (deny_non_fast_forwards && !is_null_sha1(new_sha1) &&
+	    !is_null_sha1(old_sha1)) {
 		struct commit *old_commit, *new_commit;
 		struct commit_list *bases, *ent;
 
@@ -138,14 +140,22 @@ static int update(struct command *cmd)
 		return error("hook declined to update %s", name);
 	}
 
-	lock = lock_any_ref_for_update(name, old_sha1);
-	if (!lock) {
-		cmd->error_string = "failed to lock";
-		return error("failed to lock %s", name);
+	if (is_null_sha1(new_sha1)) {
+		if (delete_ref(name, old_sha1)) {
+			cmd->error_string = "failed to delete";
+			return error("failed to delete %s", name);
+		}
+		fprintf(stderr, "%s: %s -> deleted\n", name, old_hex);
+	}
+	else {
+		lock = lock_any_ref_for_update(name, old_sha1);
+		if (!lock) {
+			cmd->error_string = "failed to lock";
+			return error("failed to lock %s", name);
+		}
+		write_ref_sha1(lock, new_sha1, "push");
+		fprintf(stderr, "%s: %s -> %s\n", name, old_hex, new_hex);
 	}
-	write_ref_sha1(lock, new_sha1, "push");
-
-	fprintf(stderr, "%s: %s -> %s\n", name, old_hex, new_hex);
 	return 0;
 }
 
@@ -375,6 +385,16 @@ static void report(const char *unpack_st
 	packet_flush(1);
 }
 
+static int delete_only(struct command *cmd)
+{
+	while (cmd) {
+		if (!is_null_sha1(cmd->new_sha1))
+			return 0;
+		cmd = cmd->next;
+	}
+	return 1;
+}
+
 int main(int argc, char **argv)
 {
 	int i;
@@ -408,7 +428,10 @@ int main(int argc, char **argv)
 
 	read_head_info();
 	if (commands) {
-		const char *unpack_status = unpack();
+		const char *unpack_status = NULL;
+
+		if (!delete_only(commands))
+			unpack_status = unpack();
 		if (!unpack_status)
 			execute_commands();
 		if (pack_lockfile)
diff --git a/send-pack.c b/send-pack.c
index 4476666..328dbbc 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -271,6 +271,7 @@ static int send_pack(int in, int out, in
 	int new_refs;
 	int ret = 0;
 	int ask_for_status_report = 0;
+	int allow_deleting_refs = 0;
 	int expect_status_report = 0;
 
 	/* No funny business with the matcher */
@@ -280,6 +281,8 @@ static int send_pack(int in, int out, in
 	/* Does the other end support the reporting? */
 	if (server_supports("report-status"))
 		ask_for_status_report = 1;
+	if (server_supports("delete-refs"))
+		allow_deleting_refs = 1;
 
 	/* match them up */
 	if (!remote_tail)
@@ -299,9 +302,19 @@ static int send_pack(int in, int out, in
 	new_refs = 0;
 	for (ref = remote_refs; ref; ref = ref->next) {
 		char old_hex[60], *new_hex;
+		int delete_ref;
+
 		if (!ref->peer_ref)
 			continue;
-		if (!hashcmp(ref->old_sha1, ref->peer_ref->new_sha1)) {
+
+		delete_ref = is_null_sha1(ref->peer_ref->new_sha1);
+		if (delete_ref && !allow_deleting_refs) {
+			error("remote does not support deleting refs");
+			ret = -2;
+			continue;
+		}
+		if (!delete_ref &&
+		    !hashcmp(ref->old_sha1, ref->peer_ref->new_sha1)) {
 			if (verbose)
 				fprintf(stderr, "'%s': up-to-date\n", ref->name);
 			continue;
@@ -321,9 +334,13 @@ static int send_pack(int in, int out, in
 		 *
 		 * (3) if both new and old are commit-ish, and new is a
 		 *     descendant of old, it is OK.
+		 *
+		 * (4) regardless of all of the above, removing :B is
+		 *     always allowed.
 		 */
 
 		if (!force_update &&
+		    !delete_ref &&
 		    !is_zero_sha1(ref->old_sha1) &&
 		    !ref->force) {
 			if (!has_sha1_file(ref->old_sha1) ||
@@ -347,12 +364,8 @@ static int send_pack(int in, int out, in
 			}
 		}
 		hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
-		if (is_zero_sha1(ref->new_sha1)) {
-			error("cannot happen anymore");
-			ret = -3;
-			continue;
-		}
-		new_refs++;
+		if (!delete_ref)
+			new_refs++;
 		strcpy(old_hex, sha1_to_hex(ref->old_sha1));
 		new_hex = sha1_to_hex(ref->new_sha1);
 
@@ -366,10 +379,16 @@ static int send_pack(int in, int out, in
 		else
 			packet_write(out, "%s %s %s",
 				     old_hex, new_hex, ref->name);
-		fprintf(stderr, "updating '%s'", ref->name);
-		if (strcmp(ref->name, ref->peer_ref->name))
-			fprintf(stderr, " using '%s'", ref->peer_ref->name);
-		fprintf(stderr, "\n  from %s\n  to   %s\n", old_hex, new_hex);
+		if (delete_ref)
+			fprintf(stderr, "deleting '%s'\n", ref->name);
+		else {
+			fprintf(stderr, "updating '%s'", ref->name);
+			if (strcmp(ref->name, ref->peer_ref->name))
+				fprintf(stderr, " using '%s'",
+					ref->peer_ref->name);
+			fprintf(stderr, "\n  from %s\n  to   %s\n",
+				old_hex, new_hex);
+		}
 	}
 
 	packet_flush(out);
diff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh
index 8afb899..28744b3 100755
--- a/t/t5400-send-pack.sh
+++ b/t/t5400-send-pack.sh
@@ -64,6 +64,16 @@ test_expect_success \
 	cmp victim/.git/refs/heads/master .git/refs/heads/master
 '
 
+test_expect_success \
+        'push can be used to delete a ref' '
+	cd victim &&
+	git branch extra master &&
+	cd .. &&
+	test -f victim/.git/refs/heads/extra &&
+	git-send-pack ./victim/.git/ :extra master &&
+	! test -f victim/.git/refs/heads/extra
+'
+
 unset GIT_CONFIG GIT_CONFIG_LOCAL
 HOME=`pwd`/no-such-directory
 export HOME ;# this way we force the victim/.git/config to be used.
-- 
1.4.4.1.g77614


```
