From: Felipe Contreras <hidden> Date: 2016-06-15 23:00:41
One of the most serious recurring issues[1][2][3] with remote helpers is that
marks get out of sync. The way to analize and reproduce these wasn't trivial,
but the culprit seems to be a crash while doing `git push`. It has been known
already how exactly that happens, but no simple way how to fix it.
This is the simplest way so far; tell `git fast-export` to export the marks to
a temporary file, and move it to the right location only *after* the remote
helper has finished its job without errors.
Since the code wasn't prepared for a change like this, some reorganization
changes are needed. More changes might be welcome to further propagate the
errors properly through the code, but for the moment the errors are propagated
to the right location, in order to fix this specific problem.
[1] http://article.gmane.org/gmane.comp.version-control.git/223962
[2] http://article.gmane.org/gmane.comp.version-control.git/241707
[3] https://github.com/felipec/git/issues/56
Felipe Contreras (5):
transport-helper: remove barely used xchgline()
remote-helpers: make recvline return an error
transport-helper: propagate recvline() error pushing
transport-helper: trivial cleanup
transport-helper: fix sync issue on crashes
t/t5801-remote-helpers.sh | 17 ++++++++++-
transport-helper.c | 72 ++++++++++++++++++++++++++++-------------------
2 files changed, 59 insertions(+), 30 deletions(-)
--
1.9.1+fc3.9.gc73078e
From: Felipe Contreras <hidden> Date: 2016-06-15 23:00:41
It's only used once, we can just call the two functions inside directly.
Signed-off-by: Felipe Contreras <redacted>
---
transport-helper.c | 9 ++-------
1 file changed, 2 insertions(+), 7 deletions(-)
@@ -307,7 +301,8 @@ static int set_helper_option(struct transport *transport,quote_c_style(value,&buf,NULL,0);strbuf_addch(&buf,'\n');-xchgline(data,&buf);+sendline(data,&buf);+recvline(data,&buf);if(!strcmp(buf.buf,"ok"))ret=0;
From: Felipe Contreras <hidden> Date: 2016-06-15 23:00:41
Instead of exiting directly, make it the duty of the caller to do so.
Signed-off-by: Felipe Contreras <redacted>
---
transport-helper.c | 21 ++++++++++++++-------
1 file changed, 14 insertions(+), 7 deletions(-)
@@ -157,7 +157,8 @@ static struct child_process *get_helper(struct transport *transport)while(1){constchar*capname;intmandatory=0;-recvline(data,&buf);+if(recvline(data,&buf))+exit(128);if(!*buf.buf)break;
@@ -302,7 +303,8 @@ static int set_helper_option(struct transport *transport,strbuf_addch(&buf,'\n');sendline(data,&buf);-recvline(data,&buf);+if(recvline(data,&buf))+exit(128);if(!strcmp(buf.buf,"ok"))ret=0;
@@ -374,7 +376,8 @@ static int fetch_with_fetch(struct transport *transport,sendline(data,&buf);while(1){-recvline(data,&buf);+if(recvline(data,&buf))+exit(128);if(starts_with(buf.buf,"lock ")){constchar*name=buf.buf+5;
@@ -558,7 +561,9 @@ static int process_connect_service(struct transport *transport,gotoexit;sendline(data,&cmdbuf);-recvline_fh(input,&cmdbuf,name);+if(recvline_fh(input,&cmdbuf,name))+exit(128);+if(!strcmp(cmdbuf.buf,"")){data->no_disconnect_req=1;if(debug)
From: Felipe Contreras <hidden> Date: 2016-06-15 23:00:41
When a remote helper crashes while pushing we should revert back to the
state before the push, however, it's possible that `git fast-export`
already finished its job, and therefore has exported the marks already.
This creates a synchronization problem because from that moment on
`git fast-{import,export}` will have marks that the remote helper is not
aware of and all further commands fail (if those marks are referenced).
The fix is to tell `git fast-export` to export to a temporary file, and
only after the remote helper has finishes successfully, move to the
final destination.
Signed-off-by: Felipe Contreras <redacted>
---
t/t5801-remote-helpers.sh | 17 ++++++++++++++++-
transport-helper.c | 13 +++++++++++--
2 files changed, 27 insertions(+), 3 deletions(-)
@@ -434,7 +434,7 @@ static int get_exporter(struct transport *transport,fastexport->argv[argc++]=data->signed_tags?"--signed-tags=verbatim":"--signed-tags=warn-strip";if(data->export_marks){-strbuf_addf(&tmp,"--export-marks=%s",data->export_marks);+strbuf_addf(&tmp,"--export-marks=%s.tmp",data->export_marks);fastexport->argv[argc++]=strbuf_detach(&tmp,NULL);}if(data->import_marks){
@@ -901,7 +901,16 @@ static int push_refs_with_export(struct transport *transport,if(finish_command(&exporter))die("Error while running fast-export");-returnpush_update_refs_status(data,remote_refs);+if(push_update_refs_status(data,remote_refs))+return1;++if(data->export_marks){+strbuf_addf(&buf,"%s.tmp",data->export_marks);+rename(buf.buf,data->export_marks);+strbuf_release(&buf);+}++return0;}staticintpush_refs(structtransport*transport,
From: Felipe Contreras <hidden> Date: 2016-06-15 23:00:41
It's simpler to store the file names directly, and form the fast-export
arguments only when needed, and re-use the same strbuf with a format.
Signed-off-by: Felipe Contreras <redacted>
---
transport-helper.c | 23 +++++++++++------------
1 file changed, 11 insertions(+), 12 deletions(-)
@@ -429,6 +423,7 @@ static int get_exporter(struct transport *transport,structchild_process*helper=get_helper(transport);intargc=0,i;memset(fastexport,0,sizeof(*fastexport));+structstrbuftmp=STRBUF_INIT;/* we need to duplicate helper->in because we want to use it after*fastexportisdonewithit.*/
@@ -438,10 +433,14 @@ static int get_exporter(struct transport *transport,fastexport->argv[argc++]="--use-done-feature";fastexport->argv[argc++]=data->signed_tags?"--signed-tags=verbatim":"--signed-tags=warn-strip";-if(data->export_marks)-fastexport->argv[argc++]=data->export_marks;-if(data->import_marks)-fastexport->argv[argc++]=data->import_marks;+if(data->export_marks){+strbuf_addf(&tmp,"--export-marks=%s",data->export_marks);+fastexport->argv[argc++]=strbuf_detach(&tmp,NULL);+}+if(data->import_marks){+strbuf_addf(&tmp,"--import-marks=%s",data->import_marks);+fastexport->argv[argc++]=strbuf_detach(&tmp,NULL);+}for(i=0;i<revlist_args->nr;i++)fastexport->argv[argc++]=revlist_args->items[i].string;
From: Felipe Contreras <hidden> Date: 2016-06-15 23:00:41
It's cleaner, and will allow us to do something sensible on errors
later.
Signed-off-by: Felipe Contreras <redacted>
---
transport-helper.c | 18 +++++++++++-------
1 file changed, 11 insertions(+), 7 deletions(-)
@@ -840,8 +846,7 @@ static int push_refs_with_push(struct transport *transport,sendline(data,&buf);strbuf_release(&buf);-push_update_refs_status(data,remote_refs);-return0;+returnpush_update_refs_status(data,remote_refs);}staticintpush_refs_with_export(structtransport*transport,
@@ -897,8 +902,7 @@ static int push_refs_with_export(struct transport *transport,if(finish_command(&exporter))die("Error while running fast-export");-push_update_refs_status(data,remote_refs);-return0;+returnpush_update_refs_status(data,remote_refs);}staticintpush_refs(structtransport*transport,
From: Junio C Hamano <hidden> Date: 2016-06-15 23:00:42
Felipe Contreras [off-list ref] writes:
quoted hunk
It's simpler to store the file names directly, and form the fast-export
arguments only when needed, and re-use the same strbuf with a format.
Signed-off-by: Felipe Contreras <redacted>
---
transport-helper.c | 23 +++++++++++------------
1 file changed, 11 insertions(+), 12 deletions(-)
@@ -429,6 +423,7 @@ static int get_exporter(struct transport *transport,structchild_process*helper=get_helper(transport);intargc=0,i;memset(fastexport,0,sizeof(*fastexport));+structstrbuftmp=STRBUF_INIT;
Will fix up decl-after-stmt while queuing.
quoted hunk
/* we need to duplicate helper->in because we want to use it after
* fastexport is done with it. */
@@ -438,10 +433,14 @@ static int get_exporter(struct transport *transport, fastexport->argv[argc++] = "--use-done-feature"; fastexport->argv[argc++] = data->signed_tags ? "--signed-tags=verbatim" : "--signed-tags=warn-strip";- if (data->export_marks)- fastexport->argv[argc++] = data->export_marks;- if (data->import_marks)- fastexport->argv[argc++] = data->import_marks;+ if (data->export_marks) {+ strbuf_addf(&tmp, "--export-marks=%s", data->export_marks);+ fastexport->argv[argc++] = strbuf_detach(&tmp, NULL);+ }+ if (data->import_marks) {+ strbuf_addf(&tmp, "--import-marks=%s", data->import_marks);+ fastexport->argv[argc++] = strbuf_detach(&tmp, NULL);+ } for (i = 0; i < revlist_args->nr; i++) fastexport->argv[argc++] = revlist_args->items[i].string;
From: Junio C Hamano <hidden> Date: 2016-06-15 23:00:42
Felipe Contreras [off-list ref] writes:
When a remote helper crashes while pushing we should revert back to the
state before the push, however, it's possible that `git fast-export`
already finished its job, and therefore has exported the marks already.
This creates a synchronization problem because from that moment on
`git fast-{import,export}` will have marks that the remote helper is not
aware of and all further commands fail (if those marks are referenced).
The fix is to tell `git fast-export` to export to a temporary file, and
only after the remote helper has finishes successfully, move to the
final destination.
Signed-off-by: Felipe Contreras <redacted>
---
This seems to be based on a somewhat older codebase; I tried to be
careful while adjusting the patch to the current codebase, but
please give it an eyeball to see if I didn't make any silly mistake
when I push today's integration result out in a few hours.
Thanks.
@@ -434,7 +434,7 @@ static int get_exporter(struct transport *transport,fastexport->argv[argc++]=data->signed_tags?"--signed-tags=verbatim":"--signed-tags=warn-strip";if(data->export_marks){-strbuf_addf(&tmp,"--export-marks=%s",data->export_marks);+strbuf_addf(&tmp,"--export-marks=%s.tmp",data->export_marks);fastexport->argv[argc++]=strbuf_detach(&tmp,NULL);}if(data->import_marks){
@@ -901,7 +901,16 @@ static int push_refs_with_export(struct transport *transport,if(finish_command(&exporter))die("Error while running fast-export");-returnpush_update_refs_status(data,remote_refs);+if(push_update_refs_status(data,remote_refs))+return1;++if(data->export_marks){+strbuf_addf(&buf,"%s.tmp",data->export_marks);+rename(buf.buf,data->export_marks);+strbuf_release(&buf);+}++return0;}staticintpush_refs(structtransport*transport,
From: Junio C Hamano <hidden> Date: 2016-06-15 23:00:44
As I have said in the recent What's cooking reports, the original
posted here were based on older codebase and needed to be rebased,
but it had some conflicts and I wanted to see the result double
checked by the original author before we can merge it to 'next',
cooked there and hopefully merged to 'master' before tagging -rc1.
So here is the series that has been queued in 'pu' for the past
several days.
Felipe, can you double check it?
Thanks.
Felipe Contreras (5):
transport-helper: remove barely used xchgline()
remote-helpers: make recvline return an error
transport-helper: propagate recvline() error pushing
transport-helper: trivial cleanup
transport-helper: fix sync issue on crashes
t/t5801-remote-helpers.sh | 20 ++++++++++++-
transport-helper.c | 73 ++++++++++++++++++++++++++++-------------------
2 files changed, 63 insertions(+), 30 deletions(-)
--
1.9.2-459-g68773ac
From: Junio C Hamano <hidden> Date: 2016-06-15 23:00:44
From: Felipe Contreras <redacted>
Instead of exiting directly, make it the duty of the caller to do so.
Signed-off-by: Felipe Contreras <redacted>
Signed-off-by: Junio C Hamano <redacted>
---
transport-helper.c | 21 ++++++++++++++-------
1 file changed, 14 insertions(+), 7 deletions(-)
@@ -157,7 +157,8 @@ static struct child_process *get_helper(struct transport *transport)while(1){constchar*capname;intmandatory=0;-recvline(data,&buf);+if(recvline(data,&buf))+exit(128);if(!*buf.buf)break;
@@ -302,7 +303,8 @@ static int set_helper_option(struct transport *transport,strbuf_addch(&buf,'\n');sendline(data,&buf);-recvline(data,&buf);+if(recvline(data,&buf))+exit(128);if(!strcmp(buf.buf,"ok"))ret=0;
@@ -374,7 +376,8 @@ static int fetch_with_fetch(struct transport *transport,sendline(data,&buf);while(1){-recvline(data,&buf);+if(recvline(data,&buf))+exit(128);if(starts_with(buf.buf,"lock ")){constchar*name=buf.buf+5;
@@ -558,7 +561,9 @@ static int process_connect_service(struct transport *transport,gotoexit;sendline(data,&cmdbuf);-recvline_fh(input,&cmdbuf,name);+if(recvline_fh(input,&cmdbuf,name))+exit(128);+if(!strcmp(cmdbuf.buf,"")){data->no_disconnect_req=1;if(debug)
From: Junio C Hamano <hidden> Date: 2016-06-15 23:00:44
From: Felipe Contreras <redacted>
It's only used once, we can just call the two functions inside directly.
Signed-off-by: Felipe Contreras <redacted>
Signed-off-by: Junio C Hamano <redacted>
---
transport-helper.c | 9 ++-------
1 file changed, 2 insertions(+), 7 deletions(-)
@@ -307,7 +301,8 @@ static int set_helper_option(struct transport *transport,quote_c_style(value,&buf,NULL,0);strbuf_addch(&buf,'\n');-xchgline(data,&buf);+sendline(data,&buf);+recvline(data,&buf);if(!strcmp(buf.buf,"ok"))ret=0;
From: Junio C Hamano <hidden> Date: 2016-06-15 23:00:44
From: Felipe Contreras <redacted>
It's cleaner, and will allow us to do something sensible on errors
later.
Signed-off-by: Felipe Contreras <redacted>
Signed-off-by: Junio C Hamano <redacted>
---
transport-helper.c | 18 +++++++++++-------
1 file changed, 11 insertions(+), 7 deletions(-)
@@ -847,8 +853,7 @@ static int push_refs_with_push(struct transport *transport,sendline(data,&buf);strbuf_release(&buf);-push_update_refs_status(data,remote_refs,flags);-return0;+returnpush_update_refs_status(data,remote_refs,flags);}staticintpush_refs_with_export(structtransport*transport,
@@ -906,8 +911,7 @@ static int push_refs_with_export(struct transport *transport,if(finish_command(&exporter))die("Error while running fast-export");-push_update_refs_status(data,remote_refs,flags);-return0;+returnpush_update_refs_status(data,remote_refs,flags);}staticintpush_refs(structtransport*transport,
From: Junio C Hamano <hidden> Date: 2016-06-15 23:00:44
From: Felipe Contreras <redacted>
It's simpler to store the file names directly, and form the fast-export
arguments only when needed, and re-use the same strbuf with a format.
Signed-off-by: Felipe Contreras <redacted>
Signed-off-by: Junio C Hamano <redacted>
---
transport-helper.c | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
@@ -428,6 +422,8 @@ static int get_exporter(struct transport *transport,structhelper_data*data=transport->data;structchild_process*helper=get_helper(transport);intargc=0,i;+structstrbuftmp=STRBUF_INIT;+memset(fastexport,0,sizeof(*fastexport));/* we need to duplicate helper->in because we want to use it after
@@ -438,10 +434,14 @@ static int get_exporter(struct transport *transport,fastexport->argv[argc++]="--use-done-feature";fastexport->argv[argc++]=data->signed_tags?"--signed-tags=verbatim":"--signed-tags=warn-strip";-if(data->export_marks)-fastexport->argv[argc++]=data->export_marks;-if(data->import_marks)-fastexport->argv[argc++]=data->import_marks;+if(data->export_marks){+strbuf_addf(&tmp,"--export-marks=%s",data->export_marks);+fastexport->argv[argc++]=strbuf_detach(&tmp,NULL);+}+if(data->import_marks){+strbuf_addf(&tmp,"--import-marks=%s",data->import_marks);+fastexport->argv[argc++]=strbuf_detach(&tmp,NULL);+}for(i=0;i<revlist_args->nr;i++)fastexport->argv[argc++]=revlist_args->items[i].string;
From: Junio C Hamano <hidden> Date: 2016-06-15 23:00:44
From: Felipe Contreras <redacted>
When a remote helper crashes while pushing we should revert back to the
state before the push, however, it's possible that `git fast-export`
already finished its job, and therefore has exported the marks already.
This creates a synchronization problem because from that moment on
`git fast-{import,export}` will have marks that the remote helper is not
aware of and all further commands fail (if those marks are referenced).
The fix is to tell `git fast-export` to export to a temporary file, and
only after the remote helper has finishes successfully, move to the
final destination.
Signed-off-by: Felipe Contreras <redacted>
Signed-off-by: Junio C Hamano <redacted>
---
t/t5801-remote-helpers.sh | 20 +++++++++++++++++++-
transport-helper.c | 13 +++++++++++--
2 files changed, 30 insertions(+), 3 deletions(-)
@@ -435,7 +435,7 @@ static int get_exporter(struct transport *transport,fastexport->argv[argc++]=data->signed_tags?"--signed-tags=verbatim":"--signed-tags=warn-strip";if(data->export_marks){-strbuf_addf(&tmp,"--export-marks=%s",data->export_marks);+strbuf_addf(&tmp,"--export-marks=%s.tmp",data->export_marks);fastexport->argv[argc++]=strbuf_detach(&tmp,NULL);}if(data->import_marks){
@@ -911,7 +911,16 @@ static int push_refs_with_export(struct transport *transport,if(finish_command(&exporter))die("Error while running fast-export");-returnpush_update_refs_status(data,remote_refs,flags);+if(push_update_refs_status(data,remote_refs,flags))+return1;++if(data->export_marks){+strbuf_addf(&buf,"%s.tmp",data->export_marks);+rename(buf.buf,data->export_marks);+strbuf_release(&buf);+}++return0;}staticintpush_refs(structtransport*transport,
From: Felipe Contreras <hidden> Date: 2016-06-15 23:00:44
Junio C Hamano wrote:
As I have said in the recent What's cooking reports, the original
posted here were based on older codebase and needed to be rebased,
but it had some conflicts and I wanted to see the result double
checked by the original author before we can merge it to 'next',
cooked there and hopefully merged to 'master' before tagging -rc1.
So here is the series that has been queued in 'pu' for the past
several days.
Felipe, can you double check it?
These patches don't help much, I did and interdiff with my own fixes and this
is the result: