{"thread":{"id":"4003","subject":"[PATCH] git builtin \"push\"","startedAt":"2006-04-30T04:22:49Z","lastAt":"2006-04-30T21:47:48Z","messageCount":9,"participants":["Linus Torvalds","Junio C Hamano","sean","Jeff Garzik"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"19274","messageId":"Pine.LNX.4.64.0604292111570.9901@g5.osdl.org","threadId":"4003","inReplyTo":null,"subject":"[PATCH] git builtin \"push\"","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-04-30T04:22:49Z","receivedAt":"2006-04-30T04:22:49Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\nThis adds a builtin \"push\" command, which is largely just a C'ification of \nthe \"git-push.sh\" script.\n\nNow, the reason I did it as a built-in is partly because it's yet another \nstep on relying less on shell, but it's actually mostly because I've \nwanted to be able to push to _multiple_ repositories, and the most obvious \nand simplest interface for that would seem be to just have a \"remotes\" \nfile that has multiple URL entries.\n\n(For \"pull\", having multiple entries should either just select the first \none, or you could fall back on the others on failure - your choice).\n\nAnd quite frankly, it just became too damn messy to do that in shell. \nBesides, we actually have a fair amount of infrastructure in C, so it just \nwasn't that hard to do.\n\nOf course, this is almost totally untested. It probably doesn't work for \nanything but the one trial I threw at it. \"Simple\" doesn't necessarily \nmean \"obviously correct\".\n\nSigned-off-by: Linus Torvalds <torvalds@osdl.org>\n---\n\nComments?  I wrote it so that it _should_ be fairly easy to re-use at \nleast the branches/remotes helper functions for a built-in \"git fetch\" as \nwell. But I didn't have the multi-URI issue with anything but pushing.\n\nJunio - I currently have a \"push-all\" script in each of the repos I \nmaintain. That has worked, but I'd just rather do something like\n\n\tgit push all\n\ninstead, and have it pick up the list of URI's from .git/remotes/all.\n\nHow do you do multi-targeted pushes?\n\ndiff --git a/Makefile b/Makefile\nindex 8ce27a6..9919992 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -214,7 +214,7 @@ LIB_OBJS = \\\n \t$(DIFF_OBJS)\n \n BUILTIN_OBJS = \\\n-\tbuiltin-log.o builtin-help.o\n+\tbuiltin-log.o builtin-help.o builtin-push.o\n \n GITLIBS = $(LIB_FILE) $(XDIFF_LIB)\n LIBS = $(GITLIBS) -lz\ndiff --git a/builtin-push.c b/builtin-push.c\nnew file mode 100644\nindex 0000000..a0c1caa\n--- /dev/null\n+++ b/builtin-push.c\n@@ -0,0 +1,255 @@\n+/*\n+ * \"git push\"\n+ */\n+#include \"cache.h\"\n+#include \"refs.h\"\n+#include \"run-command.h\"\n+#include \"builtin.h\"\n+\n+#define MAX_URI (16)\n+\n+static const char push_usage[] = \"git push [--all] [--tags] [--force] <repository> [<refspec>...]\";\n+\n+static int all = 0, tags = 0, force = 0, thin = 1;\n+static const char *execute = NULL;\n+\n+#define BUF_SIZE (2084)\n+static char buffer[BUF_SIZE];\n+\n+static const char **refspec = NULL;\n+static int refspec_nr = 0;\n+\n+static void add_refspec(const char *ref)\n+{\n+\tint nr = refspec_nr + 1;\n+\trefspec = xrealloc(refspec, nr * sizeof(char *));\n+\trefspec[nr-1] = ref;\n+\trefspec_nr = nr;\n+}\n+\n+static int expand_one_ref(const char *ref, const unsigned char *sha1)\n+{\n+\t/* Ignore the \"refs/\" at the beginning of the refname */\n+\tref += 5;\n+\n+\tif (strncmp(ref, \"tags/\", 5))\n+\t\treturn 0;\n+\n+\tadd_refspec(strdup(ref));\n+\treturn 0;\n+}\n+\n+static void expand_refspecs(void)\n+{\n+\tif (all) {\n+\t\tif (refspec_nr)\n+\t\t\tdie(\"cannot mix '--all' and a refspec\");\n+\n+\t\t/*\n+\t\t * No need to expand \"--all\" - we'll just use\n+\t\t * the \"--all\" flag to send-pack\n+\t\t */\n+\t\treturn;\n+\t}\n+\tif (!tags)\n+\t\treturn;\n+\tfor_each_ref(expand_one_ref);\n+}\n+\n+static void set_refspecs(const char **refs, int nr)\n+{\n+\tif (nr) {\n+\t\tsize_t bytes = nr * sizeof(char *);\n+\n+\t\trefspec = xrealloc(refspec, bytes);\n+\t\tmemcpy(refspec, refs, bytes);\n+\t\trefspec_nr = nr;\n+\t}\n+\texpand_refspecs();\n+}\n+\n+static int get_remotes_uri(const char *repo, const char *uri[MAX_URI])\n+{\n+\tint n = 0;\n+\tFILE *f = fopen(git_path(\"remotes/%s\", repo), \"r\");\n+\n+\tif (!f)\n+\t\treturn -1;\n+\twhile (fgets(buffer, BUF_SIZE, f)) {\n+\t\tchar *s, *p;\n+\n+\t\tif (strncmp(\"URL: \", buffer, 5))\n+\t\t\tcontinue;\n+\t\ts = buffer + 5;\n+\n+\t\t/* Remove whitespace at the head.. */\n+\t\twhile (isspace(*s))\n+\t\t\ts++;\n+\t\tif (!*s)\n+\t\t\tcontinue;\n+\n+\t\t/* ..and at the end */\n+\t\tp = s + strlen(s);\n+\t\twhile (isspace(p[-1]))\n+\t\t\t*--p = 0;\n+\n+\t\turi[n++] = strdup(s);\n+\t\tif (n == MAX_URI)\n+\t\t\tbreak;\n+\t}\n+\tfclose(f);\n+\tif (!n)\n+\t\tdie(\"remote '%s' has no URL\", repo);\n+\treturn n;\n+}\n+\n+static int get_branches_uri(const char *repo, const char *uri[MAX_URI])\n+{\n+\tconst char *slash = strchr(repo, '/');\n+\tint n = slash ? slash - repo : 1000;\n+\tFILE *f = fopen(git_path(\"branches/%.*s\", n, repo), \"r\");\n+\tchar *s, *p;\n+\tint len;\n+\n+\tif (!f)\n+\t\treturn 0;\n+\ts = fgets(buffer, BUF_SIZE, f);\n+\tfclose(f);\n+\tif (!s)\n+\t\treturn 0;\n+\twhile (isspace(*s))\n+\t\ts++;\n+\tif (!*s)\n+\t\treturn 0;\n+\tp = s + strlen(s);\n+\twhile (isspace(p[-1]))\n+\t\t*--p = 0;\n+\tlen = p - s;\n+\tif (slash)\n+\t\tlen += strlen(slash);\n+\tp = xmalloc(len + 1);\n+\tstrcpy(p, s);\n+\tif (slash)\n+\t\tstrcat(p, slash);\n+\turi[0] = p;\n+\treturn 1;\n+}\n+\n+static int get_uri(const char *repo, const char *uri[MAX_URI])\n+{\n+\tint n;\n+\n+\tif (*repo != '/') {\n+\t\tn = get_remotes_uri(repo, uri);\n+\t\tif (n > 0)\n+\t\t\treturn n;\n+\n+\t\tn = get_branches_uri(repo, uri);\n+\t\tif (n > 0)\n+\t\t\treturn n;\n+\t}\n+\n+\turi[0] = repo;\n+\treturn 1;\n+}\n+\n+static int do_push(const char *repo)\n+{\n+\tconst char *uri[MAX_URI];\n+\tint i, n = get_uri(repo, uri);\n+\tint remote;\n+\tconst char **argv;\n+\tint argc;\n+\n+\tn = get_uri(repo, uri);\n+\tif (n <= 0)\n+\t\tdie(\"bad repository '%s'\", repo);\n+\n+\targv = xmalloc((refspec_nr + 10) * sizeof(char *));\n+\targv[0] = \"dummy-send-pack\";\n+\targc = 1;\n+\tif (all)\n+\t\targv[argc++] = \"--all\";\n+\tif (force)\n+\t\targv[argc++] = \"--force\";\n+\tif (execute)\n+\t\targv[argc++] = execute;\n+\tif (thin)\n+\t\targv[argc++] = \"--thin\";\n+\tremote = argc;\n+\targv[argc++] = \"dummy-remote\";\n+\twhile (refspec_nr--)\n+\t\targv[argc++] = *refspec++;\n+\targv[argc] = NULL;\n+\n+\tfor (i = 0; i < n; i++) {\n+\t\tint error;\n+\t\tconst char *dest = uri[i];\n+\t\tconst char *sender = \"git-send-pack\";\n+\t\tif (!strncmp(dest, \"http://\", 7) ||\n+\t\t    !strncmp(dest, \"https://\", 8))\n+\t\t\tsender = \"git-http-push\";\n+\t\targv[0] = sender;\n+\t\targv[remote] = dest;\n+\t\terror = run_command_v(argc, argv);\n+\t\tif (!error)\n+\t\t\tcontinue;\n+\t\tswitch (error) {\n+\t\tcase -ERR_RUN_COMMAND_FORK:\n+\t\t\tdie(\"unable to fork for %s\", sender);\n+\t\tcase -ERR_RUN_COMMAND_EXEC:\n+\t\t\tdie(\"unable to exec %s\", sender);\n+\t\tcase -ERR_RUN_COMMAND_WAITPID:\n+\t\tcase -ERR_RUN_COMMAND_WAITPID_WRONG_PID:\n+\t\tcase -ERR_RUN_COMMAND_WAITPID_SIGNAL:\n+\t\tcase -ERR_RUN_COMMAND_WAITPID_NOEXIT:\n+\t\t\tdie(\"%s died with strange error\", sender);\n+\t\tdefault:\n+\t\t\treturn -error;\n+\t\t}\n+\t}\n+\treturn 0;\n+}\n+\n+int cmd_push(int argc, const char **argv, char **envp)\n+{\n+\tint i;\n+\tconst char *repo = \"origin\";\t// default repository\n+\n+\tfor (i = 1; i < argc; i++) {\n+\t\tconst char *arg = argv[i];\n+\n+\t\tif (arg[0] != '-') {\n+\t\t\trepo = arg;\n+\t\t\ti++;\n+\t\t\tbreak;\n+\t\t}\n+\t\tif (!strcmp(arg, \"--all\")) {\n+\t\t\tall = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(arg, \"--tags\")) {\n+\t\t\ttags = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(arg, \"--force\")) {\n+\t\t\tforce = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(arg, \"--thin\")) {\n+\t\t\tthin = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(arg, \"--no-thin\")) {\n+\t\t\tthin = 0;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strncmp(arg, \"--exec=\", 7)) {\n+\t\t\texecute = arg;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tusage(push_usage);\n+\t}\n+\tset_refspecs(argv + i, argc - i);\n+\treturn do_push(repo);\n+}\ndiff --git a/builtin.h b/builtin.h\nindex 47408a0..94fa9b5 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -20,4 +20,6 @@ extern int cmd_whatchanged(int argc, con\n extern int cmd_show(int argc, const char **argv, char **envp);\n extern int cmd_log(int argc, const char **argv, char **envp);\n \n+extern int cmd_push(int argc, const char **argv, char **envp);\n+\n #endif\ndiff --git a/git.c b/git.c\nindex 01b7e28..fd479e9 100644\n--- a/git.c\n+++ b/git.c\n@@ -46,6 +46,7 @@ static void handle_internal_command(int \n \t\t{ \"log\", cmd_log },\n \t\t{ \"whatchanged\", cmd_whatchanged },\n \t\t{ \"show\", cmd_show },\n+\t\t{ \"push\", cmd_push },\n \t};\n \tint i;\n \n"},{"id":"19276","messageId":"7vwtd7zgjx.fsf@assigned-by-dhcp.cox.net","threadId":"4003","inReplyTo":"Pine.LNX.4.64.0604292111570.9901@g5.osdl.org","subject":"Re: [PATCH] git builtin \"push\"","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-04-30T07:08:34Z","receivedAt":"2006-04-30T07:08:34Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@osdl.org> writes:\n\n> Junio - I currently have a \"push-all\" script in each of the repos I \n> maintain. That has worked, but I'd just rather do something like\n>\n> \tgit push all\n>\n> instead, and have it pick up the list of URI's from .git/remotes/all.\n\nMakes sense.  I am still somewhat drunk (cachaça -- agua de\nbeber -- pretty good but strong stuff), so I will look at the\npatch tomorrow, but...\n\n> How do you do multi-targeted pushes?\n\nI do not.  At least not that often.  I merge things up, test all\nof \"master\", \"maint\", \"next\", and \"pu\" locally (the Meta/Doit\nscript, available after checking out \"todo\" branch in Meta/\nsubdirectory), then \"git push ko-private\" to push into ~/git of\nthe kernel.org machine and do \"all branches\" test again\n(Meta/DoKernelOrg script).  Only after all that passes, I do a\n\"git push ko\" from my local machine to push into the public\narea, /pub/scm/git/git.git, of the kernel.org machine.\n\nFor Solaris and Cygwin testing, only occasionally, I do not push\nbut pull from the machines at work.  This is because I want to\ncatch breakage of pull side myself if there is one.\n\nThe only time I do multi-target push is just before cutting a\nrelease.  In addition to \"git push ko-private\" to push into the\nprimary Opteron machine, I push to \"git push ko-old-private\" to\npush into the i386 machine at kernel.org, \"Meta/DoKernelOrg\nmaster\" (or \"maint\") to prepare RPMs for i386 there, and another\n\"Meta/DoKernelOrg master\" (or \"maint\") on the Opteron to\nprepare RPMs for x86-64.  So personally I never felt the need\nfor a multi-target push.\n"},{"id":"19280","messageId":"BAYC1-PASMTP011ED54A1802BC6DF017AAAEB00@CEZ.ICE","threadId":"4003","inReplyTo":"Pine.LNX.4.64.0604292111570.9901@g5.osdl.org","subject":"Re: [PATCH] git builtin \"push\"","fromName":"sean","fromEmail":"seanlkml@sympatico.ca","sentAt":"2006-04-30T09:40:49Z","receivedAt":"2006-04-30T09:40:49Z","isPatch":true,"sender":{"key":"seanlkml@sympatico.ca","avatar":"https://gravatar.com/avatar/f92923f54fc08c401fc59b71829d4b89e9b8087fbba45ff87c82e6a83aee02ae?d=mp&s=160"},"body":"On Sat, 29 Apr 2006 21:22:49 -0700 (PDT)\nLinus Torvalds <torvalds@osdl.org> wrote:\n\n> Comments?  I wrote it so that it _should_ be fairly easy to re-use at \n> least the branches/remotes helper functions for a built-in \"git fetch\" as \n> well. But I didn't have the multi-URI issue with anything but pushing.\n\n\n> +\tif (strncmp(ref, \"tags/\", 5))\n> +\t\treturn 0;\n[...]\n> +\tfor_each_ref(expand_one_ref);\n\nHow about a for_each_tag() function?\n\n\n> +\tint i, n = get_uri(repo, uri);\n[...]\n> +\tn = get_uri(repo, uri);\n> +\tif (n <= 0)\n> +\t\tdie(\"bad repository '%s'\", repo);\n\nget_uri() called twice.\n\n\nThe patch looks good and is easy to read, but wouldn't it be better\nto require new c code to be thread safe and not leak memory?  Assuming\nrun-once-and-exit is making it difficult to push into a library.\n\nSean\n\nbuiltin-push.c |   11 +++--------\nrefs.c         |    5 +++++\nrefs.h         |    1 +\n3 files changed, 9 insertions(+), 8 deletions(-)\n\n0e0e3cff71d65ac4cdc560ae143aded03acb4ceb\ndiff --git a/builtin-push.c b/builtin-push.c\nindex a0c1caa..0d74ed1 100644\n--- a/builtin-push.c\n+++ b/builtin-push.c\n@@ -31,10 +31,6 @@ static int expand_one_ref(const char *re\n {\n \t/* Ignore the \"refs/\" at the beginning of the refname */\n \tref += 5;\n-\n-\tif (strncmp(ref, \"tags/\", 5))\n-\t\treturn 0;\n-\n \tadd_refspec(strdup(ref));\n \treturn 0;\n }\n@@ -51,9 +47,8 @@ static void expand_refspecs(void)\n \t\t */\n \t\treturn;\n \t}\n-\tif (!tags)\n-\t\treturn;\n-\tfor_each_ref(expand_one_ref);\n+\tif (tags)\n+\t\tfor_each_tag(expand_one_ref);\n }\n \n static void set_refspecs(const char **refs, int nr)\n@@ -156,7 +151,7 @@ static int get_uri(const char *repo, con\n static int do_push(const char *repo)\n {\n \tconst char *uri[MAX_URI];\n-\tint i, n = get_uri(repo, uri);\n+\tint i, n;\n \tint remote;\n \tconst char **argv;\n \tint argc;\ndiff --git a/refs.c b/refs.c\nindex 03398cc..c5a2dd0 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -178,6 +178,11 @@ int head_ref(int (*fn)(const char *path,\n \treturn 0;\n }\n \n+int for_each_tag(int (*fn)(const char *path, const unsigned char *sha1))\n+{\n+\treturn do_for_each_ref(\"refs/tags\", fn);\n+}\n+\n int for_each_ref(int (*fn)(const char *path, const unsigned char *sha1))\n {\n \treturn do_for_each_ref(\"refs\", fn);\ndiff --git a/refs.h b/refs.h\nindex 2625596..b74cd4d 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -7,6 +7,7 @@ #define REFS_H\n  */\n extern int head_ref(int (*fn)(const char *path, const unsigned char *sha1));\n extern int for_each_ref(int (*fn)(const char *path, const unsigned char *sha1));\n+extern int for_each_tag(int (*fn)(const char *path, const unsigned char *sha1));\n \n /** Reads the refs file specified into sha1 **/\n extern int get_ref_sha1(const char *ref, unsigned char *sha1);\n-- \n1.3.1.g9c203\n"},{"id":"19301","messageId":"445516F5.1090204@garzik.org","threadId":"4003","inReplyTo":"Pine.LNX.4.64.0604292111570.9901@g5.osdl.org","subject":"Re: [PATCH] git builtin \"push\"","fromName":"Jeff Garzik","fromEmail":"jeff@garzik.org","sentAt":"2006-04-30T19:58:45Z","receivedAt":"2006-04-30T19:58:45Z","isPatch":true,"sender":{"key":"jeff@garzik.org","avatar":null},"body":"Linus Torvalds wrote:\n> Now, the reason I did it as a built-in is partly because it's yet another \n> step on relying less on shell, but it's actually mostly because I've \n> wanted to be able to push to _multiple_ repositories, and the most obvious \n> and simplest interface for that would seem be to just have a \"remotes\" \n> file that has multiple URL entries.\n\nThanks!  This is why I still use rsync, even though everybody and their \nmother tells me \"Linus says rsync is deprecated.\"\n\nFact is, rsync is still the best to push a _bunch_ of branches, in the \nsame tree, all in one command.  In an attempt to get away from rsync, I \neven tried putting multiple git:// entries into .git/remotes/origin, \nthen doing \"git push\" with no args, without success.\n\nEach time I have a non-trivial libata update, I've updated twelve (12) \nbranches, and think its highly silly to have to run\n\n\turl=git://git.kernel.org/...jgarzik/...\n\tgit push $url $branch:$branch\n\ntwelve times.\n\n\tJeff\n"},{"id":"19302","messageId":"Pine.LNX.4.64.0604301303010.5231@g5.osdl.org","threadId":"4003","inReplyTo":"445516F5.1090204@garzik.org","subject":"Re: [PATCH] git builtin \"push\"","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-04-30T20:07:42Z","receivedAt":"2006-04-30T20:07:42Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sun, 30 Apr 2006, Jeff Garzik wrote:\n\n> Linus Torvalds wrote:\n> > Now, the reason I did it as a built-in is partly because it's yet another\n> > step on relying less on shell, but it's actually mostly because I've wanted\n> > to be able to push to _multiple_ repositories, and the most obvious and\n> > simplest interface for that would seem be to just have a \"remotes\" file that\n> > has multiple URL entries.\n> \n> Thanks!  This is why I still use rsync, even though everybody and their mother\n> tells me \"Linus says rsync is deprecated.\"\n\nNo. You're using rsync because you're actively doing something _wrong_.\n\n> Fact is, rsync is still the best to push a _bunch_ of branches, in the same\n> tree, all in one command.\n\nNot so. My builtin thing doesn't help at all, because it doesn't _need_ to \nhelp that case. You can already push as many branches as you want with the \nold \"git push\".\n\nMy builtin thing helps push to multiple _repositories_, not to multiple \nbranches. I push out my stuff to two separate repos at master.kernel.org, \nand one at an osdl.org server machine, because if I lose my tree, I want \nto be sure I have alternate backups.\n\n> In an attempt to get away from rsync, I even tried putting multiple \n> git:// entries into .git/remotes/origin, then doing \"git push\" with no \n> args, without success.\n\nThat's not how it works, and not how it has ever worked.\n\nThe way it works is that\n\n\t\"git push repo\"\n\nwith no branch specs will update all branches that are IN COMMON between \nyour tree and the repository you're pushing to. In other words, it's meant \nto sync up the branches that you have already pushed.\n\nIf you want to push other branches, you need to do\n\n\tgit push repo branch1 branch2 branch3 ...\n\nor\n\n\tgit push --all repo\n\nwhere the latter does exactly what it says (use \"--tags\" instead of \n\"--all\" to just send all tags).\n\nIn all cases you will find that it's a hell of a lot more efficient than \nrsync ever has been.\n\n\t\tLinus\n"},{"id":"19303","messageId":"BAYC1-PASMTP037751CEC096DA29400513AEB00@CEZ.ICE","threadId":"4003","inReplyTo":"Pine.LNX.4.64.0604301303010.5231@g5.osdl.org","subject":"Re: [PATCH] git builtin \"push\"","fromName":"sean","fromEmail":"seanlkml@sympatico.ca","sentAt":"2006-04-30T20:50:28Z","receivedAt":"2006-04-30T20:50:28Z","isPatch":true,"sender":{"key":"seanlkml@sympatico.ca","avatar":"https://gravatar.com/avatar/f92923f54fc08c401fc59b71829d4b89e9b8087fbba45ff87c82e6a83aee02ae?d=mp&s=160"},"body":"On Sun, 30 Apr 2006 13:07:42 -0700 (PDT)\nLinus Torvalds <torvalds@osdl.org> wrote:\n\n> The way it works is that\n> \n> \t\"git push repo\"\n> \n> with no branch specs will update all branches that are IN COMMON between \n> your tree and the repository you're pushing to. In other words, it's meant \n> to sync up the branches that you have already pushed.\n\nTrue, as long as you don't have any Push: lines in your .git/remotes/repo\nfile.  Any such lines act just like you gave them on the command line\nwhich afaik effectively removes the ability to say \"update all branches\nwe have in common\".\n\nSean\n"},{"id":"19304","messageId":"Pine.LNX.4.64.0604301405070.2922@g5.osdl.org","threadId":"4003","inReplyTo":"BAYC1-PASMTP037751CEC096DA29400513AEB00@CEZ.ICE","subject":"Re: [PATCH] git builtin \"push\"","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-04-30T21:05:33Z","receivedAt":"2006-04-30T21:05:33Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sun, 30 Apr 2006, sean wrote:\n\n> On Sun, 30 Apr 2006 13:07:42 -0700 (PDT)\n> Linus Torvalds <torvalds@osdl.org> wrote:\n> \n> > The way it works is that\n> > \n> > \t\"git push repo\"\n> > \n> > with no branch specs will update all branches that are IN COMMON between \n> > your tree and the repository you're pushing to. In other words, it's meant \n> > to sync up the branches that you have already pushed.\n> \n> True, as long as you don't have any Push: lines in your .git/remotes/repo\n> file.  Any such lines act just like you gave them on the command line\n> which afaik effectively removes the ability to say \"update all branches\n> we have in common\".\n\nOk, so my built-in version \"fixes\" that \"feature\". \n\n\t\tLinus\n"},{"id":"19305","messageId":"44552C6E.6090601@garzik.org","threadId":"4003","inReplyTo":"Pine.LNX.4.64.0604301303010.5231@g5.osdl.org","subject":"Re: [PATCH] git builtin \"push\"","fromName":"Jeff Garzik","fromEmail":"jeff@garzik.org","sentAt":"2006-04-30T21:30:22Z","receivedAt":"2006-04-30T21:30:22Z","isPatch":true,"sender":{"key":"jeff@garzik.org","avatar":null},"body":"Linus Torvalds wrote:\n> If you want to push other branches, you need to do\n> \n> \tgit push repo branch1 branch2 branch3 ...\n> \n> or\n> \n> \tgit push --all repo\n> \n> where the latter does exactly what it says (use \"--tags\" instead of \n> \"--all\" to just send all tags).\n\nAfter experimenting, \"--all\" does indeed provide most of the features \nthat rsync provides.  A few minor niggles:\n\n1) Doesn't propagate local branch deletions to the remote, like rsync does.\n\n2) git-push \"-f\" doesn't seem to work, but \"--force\" does.\n\n3) You still have to provide a $repo argument to 'git pull $repo'. \nWould like to list the default remote push URL in \n.git/remotes/{somefile} so that I need only to do \"git push --all\" to \nhave changes send to any number of remote servers.\n\n4) Propagation of alternatives is unclear (at least in docs).  Without \nmy current pack file pre-sharing and hardlinking, I fear needlessly \nuploading vanilla linux-2.6.git changes back to kernel.org, when I do a \npush.  Currently, pack files are downloaded _once_ from kernel.org to \nlocal, and never re-uploaded.\n\nRegards,\n\n\tJeff\n"},{"id":"19306","messageId":"7vslnuybuj.fsf@assigned-by-dhcp.cox.net","threadId":"4003","inReplyTo":"Pine.LNX.4.64.0604301405070.2922@g5.osdl.org","subject":"Re: [PATCH] git builtin \"push\"","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-04-30T21:47:48Z","receivedAt":"2006-04-30T21:47:48Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@osdl.org> writes:\n\n>> True, as long as you don't have any Push: lines in your .git/remotes/repo\n>> file.  Any such lines act just like you gave them on the command line\n>> which afaik effectively removes the ability to say \"update all branches\n>> we have in common\".\n>\n> Ok, so my built-in version \"fixes\" that \"feature\". \n>\n> \t\tLinus\n\nAlternatively/additionally you could do something like the\nattached patch.  However,...\n\nI suspect that destination repositories that you would want to\ndefine Push: and the ones you do not want to have any Push: are\nof different nature and you would not mix them.  When you do\nwant to have any Push: line for a repository, you would _never_\nwant to use the \"matching refs\" semantics for that repository.\nSo in that sense, the \"fix\" is not necessary, and fixing it\nwould probably lead to more confusion.\n\nThe Push: lines can do two related but independent things:\n\n - limit the set of refs that are published (hide the uncooked topics)\n - remap the source and destination refs (e.g. push master to origin)\n\nThe first can be done by not having Push: at all and by solely\nrelying on \"matching refs\" semantics.  Then usually you can just\nkeep doing \"git push <destination>\" until you have something new\nto add, at that point you say \"git push <destination>\n<newbranch>\" just once, and after that keep doing the \"matching\nrefs\" push.  You are having the remote repository remember what\nthe set of refs you would want there, instead of listing them on\nthe local \"Push:\" lines.  All should work well that way.\n\nI use Push: lines to push into my private, non-bare, repository\nat kernel.org machines for the second reason.  I have:\n\n\tPush: heads/master:heads/origin\n        Push: heads/next:heads/next\n        Push: +heads/pu:heads/pu\n        Push: heads/maint:heads/maint\n\nand leave the \"master\" branch checked out over there.  After\npushing, I log-in, and start my work by \"git pull . origin\".\n\nThe \"matching refs\" feature actively does a wrong thing to\nremote repositories managed this way.\n\n-- >8 --\n[PATCH] git push --common <repo>\n\nIf you usually push only specific branches, it is handy to have\nPush: lines only for those branches in your remotes/<repo> file.\nBut with that, you cannot access the underlying git-send-pack's\nability to push \"only matching refs\".  The flag --common can be\ngiven to ignore Push: lines and push only matching refs.\n\n---\ndiff --git a/git-push.sh b/git-push.sh\nindex f10cadb..381b76f 100755\n--- a/git-push.sh\n+++ b/git-push.sh\n@@ -11,6 +11,7 @@ has_exec=\n has_thin=--thin\n remote=\n do_tags=\n+do_common=\n \n while case \"$#\" in 0) break ;; esac\n do\n@@ -19,6 +20,8 @@ do\n \t\thas_all=--all ;;\n \t--tags)\n \t\tdo_tags=yes ;;\n+\t--common)\n+\t\tdo_common=yes ;;\n \t--force)\n \t\thas_force=--force ;;\n \t--exec=*)\n@@ -45,10 +48,21 @@ esac\n . git-parse-remote\n remote=$(get_remote_url \"$@\")\n \n-case \"$has_all\" in\n---all)\n+case \"$has_all,$do_common\" in\n+--all,yes)\n+\tdie \"Both --common and --all given.\" ;;\n+--all,)\n \tset x ;;\n-'')\n+,yes)\n+\tcase \"$do_tags,$#\" in\n+\t,1)\n+\t\tset x ;;\n+\tyes,*)\n+\t\tdie \"Both --common and --tags given.\" ;;\n+\t*)\n+\t\tdie \"Both --common and an explicit refspec given.\" ;;\n+\tesac ;;\n+,)\n \tcase \"$do_tags,$#\" in\n \tyes,1)\n \t\tset x $(cd \"$GIT_DIR/refs\" && find tags -type f -print) ;;\n"}]}