* [PATCH] refs.c: use a stringlist for repack_without_refs @ 2014-11-18 22:43 Stefan Beller 2014-11-18 23:06 ` Junio C Hamano 2014-11-18 23:45 ` Jonathan Nieder 0 siblings, 2 replies; 61+ messages in thread From: Stefan Beller @ 2014-11-18 22:43 UTC (permalink / raw) To: git, gitster; +Cc: Stefan Beller This patch was heavily inspired by a part of the ref-transactions-rename series[1], but people tend to dislike large series and this part is relatively easy to take out and unrelated, so I'll send it as a single patch. This patch doesn't intend any functional changes. It is just a refactoring, which replaces a char** array by a stringlist in the function repack_without_refs. [1] https://www.mail-archive.com/git@vger.kernel.org/msg60604.html Idea-by: Ronnie Sahlberg <sahlberg@google.com> Signed-off-by: Stefan Beller <sbeller@google.com> --- builtin/remote.c | 22 +++++++--------------- refs.c | 41 ++++++++++++++++++++--------------------- refs.h | 3 +-- 3 files changed, 28 insertions(+), 38 deletions(-) diff --git a/builtin/remote.c b/builtin/remote.c index 7f28f92..dca4ebf 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -750,16 +750,11 @@ static int mv(int argc, const char **argv) static int remove_branches(struct string_list *branches) { struct strbuf err = STRBUF_INIT; - const char **branch_names; int i, result = 0; - branch_names = xmalloc(branches->nr * sizeof(*branch_names)); - for (i = 0; i < branches->nr; i++) - branch_names[i] = branches->items[i].string; - if (repack_without_refs(branch_names, branches->nr, &err)) + if (repack_without_refs(branches, &err)) result |= error("%s", err.buf); strbuf_release(&err); - free(branch_names); for (i = 0; i < branches->nr; i++) { struct string_list_item *item = branches->items + i; @@ -1317,7 +1312,6 @@ static int prune_remote(const char *remote, int dry_run) int result = 0, i; struct ref_states states; struct string_list delete_refs_list = STRING_LIST_INIT_NODUP; - const char **delete_refs; const char *dangling_msg = dry_run ? _(" %s will become dangling!") : _(" %s has become dangling!"); @@ -1325,6 +1319,11 @@ static int prune_remote(const char *remote, int dry_run) memset(&states, 0, sizeof(states)); get_remote_ref_states(remote, &states, GET_REF_STATES); + for (i = 0; i < states.stale.nr; i++) + string_list_insert(&delete_refs_list, + states.stale.items[i].util); + + if (states.stale.nr) { printf_ln(_("Pruning %s"), remote); printf_ln(_("URL: %s"), @@ -1332,24 +1331,17 @@ static int prune_remote(const char *remote, int dry_run) ? states.remote->url[0] : _("(no URL)")); - delete_refs = xmalloc(states.stale.nr * sizeof(*delete_refs)); - for (i = 0; i < states.stale.nr; i++) - delete_refs[i] = states.stale.items[i].util; if (!dry_run) { struct strbuf err = STRBUF_INIT; - if (repack_without_refs(delete_refs, states.stale.nr, - &err)) + if (repack_without_refs(&delete_refs_list, &err)) result |= error("%s", err.buf); strbuf_release(&err); } - free(delete_refs); } for (i = 0; i < states.stale.nr; i++) { const char *refname = states.stale.items[i].util; - string_list_insert(&delete_refs_list, refname); - if (!dry_run) result |= delete_ref(refname, NULL, 0); diff --git a/refs.c b/refs.c index 5ff457e..2333a9b 100644 --- a/refs.c +++ b/refs.c @@ -2639,23 +2639,23 @@ static int curate_packed_ref_fn(struct ref_entry *entry, void *cb_data) return 0; } -int repack_without_refs(const char **refnames, int n, struct strbuf *err) +int repack_without_refs(struct string_list *without, struct strbuf *err) { struct ref_dir *packed; struct string_list refs_to_delete = STRING_LIST_INIT_DUP; struct string_list_item *ref_to_delete; - int i, ret, removed = 0; + int count, ret, removed = 0; assert(err); - /* Look for a packed ref */ - for (i = 0; i < n; i++) - if (get_packed_ref(refnames[i])) - break; + count = 0; + for_each_string_list_item(ref_to_delete, without) + if (get_packed_ref(ref_to_delete->string)) + count++; - /* Avoid locking if we have nothing to do */ - if (i == n) - return 0; /* no refname exists in packed refs */ + /* No refname exists in packed refs */ + if (!count) + return 0; if (lock_packed_refs(0)) { unable_to_lock_message(git_path("packed-refs"), errno, err); @@ -2664,8 +2664,8 @@ int repack_without_refs(const char **refnames, int n, struct strbuf *err) packed = get_packed_refs(&ref_cache); /* Remove refnames from the cache */ - for (i = 0; i < n; i++) - if (remove_entry(packed, refnames[i]) != -1) + for_each_string_list_item(ref_to_delete, without) + if (remove_entry(packed, ref_to_delete->string) != -1) removed = 1; if (!removed) { /* @@ -3738,10 +3738,11 @@ static int ref_update_reject_duplicates(struct ref_update **updates, int n, int ref_transaction_commit(struct ref_transaction *transaction, struct strbuf *err) { - int ret = 0, delnum = 0, i; - const char **delnames; + int ret = 0, i; int n = transaction->nr; struct ref_update **updates = transaction->updates; + struct string_list refs_to_delete = STRING_LIST_INIT_DUP; + struct string_list_item *ref_to_delete; assert(err); @@ -3753,9 +3754,6 @@ int ref_transaction_commit(struct ref_transaction *transaction, return 0; } - /* Allocate work space */ - delnames = xmalloc(sizeof(*delnames) * n); - /* Copy, sort, and reject duplicate refs */ qsort(updates, n, sizeof(*updates), ref_update_compare); if (ref_update_reject_duplicates(updates, n, err)) { @@ -3815,16 +3813,17 @@ int ref_transaction_commit(struct ref_transaction *transaction, } if (!(update->flags & REF_ISPRUNING)) - delnames[delnum++] = update->lock->ref_name; + string_list_insert(&refs_to_delete, + update->lock->ref_name); } } - if (repack_without_refs(delnames, delnum, err)) { + if (repack_without_refs(&refs_to_delete, err)) { ret = TRANSACTION_GENERIC_ERROR; goto cleanup; } - for (i = 0; i < delnum; i++) - unlink_or_warn(git_path("logs/%s", delnames[i])); + for_each_string_list_item(ref_to_delete, &refs_to_delete) + unlink_or_warn(git_path("logs/%s", ref_to_delete->string)); clear_loose_ref_cache(&ref_cache); cleanup: @@ -3833,7 +3832,7 @@ cleanup: for (i = 0; i < n; i++) if (updates[i]->lock) unlock_ref(updates[i]->lock); - free(delnames); + string_list_clear(&refs_to_delete, 0); return ret; } diff --git a/refs.h b/refs.h index 2bc3556..0416e5f 100644 --- a/refs.h +++ b/refs.h @@ -163,8 +163,7 @@ extern void rollback_packed_refs(void); */ int pack_refs(unsigned int flags); -extern int repack_without_refs(const char **refnames, int n, - struct strbuf *err); +extern int repack_without_refs(struct string_list *without, struct strbuf *err); extern int ref_exists(const char *); -- 2.2.0.rc2.5.gf7b9fb2 ^ permalink raw reply related [flat|nested] 61+ messages in thread
* Re: [PATCH] refs.c: use a stringlist for repack_without_refs 2014-11-18 22:43 [PATCH] refs.c: use a stringlist for repack_without_refs Stefan Beller @ 2014-11-18 23:06 ` Junio C Hamano 2014-11-18 23:39 ` Junio C Hamano 2014-11-18 23:45 ` Jonathan Nieder 1 sibling, 1 reply; 61+ messages in thread From: Junio C Hamano @ 2014-11-18 23:06 UTC (permalink / raw) To: Stefan Beller; +Cc: git Stefan Beller <sbeller@google.com> writes: > This patch was heavily inspired by a part of the ref-transactions-rename > series[1], but people tend to dislike large series and this part is > relatively easy to take out and unrelated, so I'll send it as a single > patch. > > This patch doesn't intend any functional changes. It is just a refactoring, > which replaces a char** array by a stringlist in the function > repack_without_refs. > > [1] https://www.mail-archive.com/git@vger.kernel.org/msg60604.html > > Idea-by: Ronnie Sahlberg <sahlberg@google.com> > Signed-off-by: Stefan Beller <sbeller@google.com> > --- > builtin/remote.c | 22 +++++++--------------- > refs.c | 41 ++++++++++++++++++++--------------------- > refs.h | 3 +-- > 3 files changed, 28 insertions(+), 38 deletions(-) In one codepath we were already using a string_list delete_refs_list anyway, so it makes sense to reuse that by movingan existing call to string_list_insert() a bit higher, instead of maintaining another array of pointers delete_refs[] to strings. OK, it simplifies the code by reducing the line count, which is a plus ;-) Sounds good. > > diff --git a/builtin/remote.c b/builtin/remote.c > index 7f28f92..dca4ebf 100644 > --- a/builtin/remote.c > +++ b/builtin/remote.c > @@ -750,16 +750,11 @@ static int mv(int argc, const char **argv) > static int remove_branches(struct string_list *branches) > { > struct strbuf err = STRBUF_INIT; > - const char **branch_names; > int i, result = 0; > > - branch_names = xmalloc(branches->nr * sizeof(*branch_names)); > - for (i = 0; i < branches->nr; i++) > - branch_names[i] = branches->items[i].string; > - if (repack_without_refs(branch_names, branches->nr, &err)) > + if (repack_without_refs(branches, &err)) > result |= error("%s", err.buf); > strbuf_release(&err); > - free(branch_names); > > for (i = 0; i < branches->nr; i++) { > struct string_list_item *item = branches->items + i; > @@ -1317,7 +1312,6 @@ static int prune_remote(const char *remote, int dry_run) > int result = 0, i; > struct ref_states states; > struct string_list delete_refs_list = STRING_LIST_INIT_NODUP; > - const char **delete_refs; > const char *dangling_msg = dry_run > ? _(" %s will become dangling!") > : _(" %s has become dangling!"); > @@ -1325,6 +1319,11 @@ static int prune_remote(const char *remote, int dry_run) > memset(&states, 0, sizeof(states)); > get_remote_ref_states(remote, &states, GET_REF_STATES); > > + for (i = 0; i < states.stale.nr; i++) > + string_list_insert(&delete_refs_list, > + states.stale.items[i].util); > + > + > if (states.stale.nr) { > printf_ln(_("Pruning %s"), remote); > printf_ln(_("URL: %s"), > @@ -1332,24 +1331,17 @@ static int prune_remote(const char *remote, int dry_run) > ? states.remote->url[0] > : _("(no URL)")); > > - delete_refs = xmalloc(states.stale.nr * sizeof(*delete_refs)); > - for (i = 0; i < states.stale.nr; i++) > - delete_refs[i] = states.stale.items[i].util; > if (!dry_run) { > struct strbuf err = STRBUF_INIT; > - if (repack_without_refs(delete_refs, states.stale.nr, > - &err)) > + if (repack_without_refs(&delete_refs_list, &err)) > result |= error("%s", err.buf); > strbuf_release(&err); > } > - free(delete_refs); > } > > for (i = 0; i < states.stale.nr; i++) { > const char *refname = states.stale.items[i].util; > > - string_list_insert(&delete_refs_list, refname); > - > if (!dry_run) > result |= delete_ref(refname, NULL, 0); > > diff --git a/refs.c b/refs.c > index 5ff457e..2333a9b 100644 > --- a/refs.c > +++ b/refs.c > @@ -2639,23 +2639,23 @@ static int curate_packed_ref_fn(struct ref_entry *entry, void *cb_data) > return 0; > } > > -int repack_without_refs(const char **refnames, int n, struct strbuf *err) > +int repack_without_refs(struct string_list *without, struct strbuf *err) > { > struct ref_dir *packed; > struct string_list refs_to_delete = STRING_LIST_INIT_DUP; > struct string_list_item *ref_to_delete; > - int i, ret, removed = 0; > + int count, ret, removed = 0; > > assert(err); > > - /* Look for a packed ref */ > - for (i = 0; i < n; i++) > - if (get_packed_ref(refnames[i])) > - break; > + count = 0; > + for_each_string_list_item(ref_to_delete, without) > + if (get_packed_ref(ref_to_delete->string)) > + count++; > > - /* Avoid locking if we have nothing to do */ > - if (i == n) > - return 0; /* no refname exists in packed refs */ > + /* No refname exists in packed refs */ > + if (!count) > + return 0; > > if (lock_packed_refs(0)) { > unable_to_lock_message(git_path("packed-refs"), errno, err); > @@ -2664,8 +2664,8 @@ int repack_without_refs(const char **refnames, int n, struct strbuf *err) > packed = get_packed_refs(&ref_cache); > > /* Remove refnames from the cache */ > - for (i = 0; i < n; i++) > - if (remove_entry(packed, refnames[i]) != -1) > + for_each_string_list_item(ref_to_delete, without) > + if (remove_entry(packed, ref_to_delete->string) != -1) > removed = 1; > if (!removed) { > /* > @@ -3738,10 +3738,11 @@ static int ref_update_reject_duplicates(struct ref_update **updates, int n, > int ref_transaction_commit(struct ref_transaction *transaction, > struct strbuf *err) > { > - int ret = 0, delnum = 0, i; > - const char **delnames; > + int ret = 0, i; > int n = transaction->nr; > struct ref_update **updates = transaction->updates; > + struct string_list refs_to_delete = STRING_LIST_INIT_DUP; > + struct string_list_item *ref_to_delete; > > assert(err); > > @@ -3753,9 +3754,6 @@ int ref_transaction_commit(struct ref_transaction *transaction, > return 0; > } > > - /* Allocate work space */ > - delnames = xmalloc(sizeof(*delnames) * n); > - > /* Copy, sort, and reject duplicate refs */ > qsort(updates, n, sizeof(*updates), ref_update_compare); > if (ref_update_reject_duplicates(updates, n, err)) { > @@ -3815,16 +3813,17 @@ int ref_transaction_commit(struct ref_transaction *transaction, > } > > if (!(update->flags & REF_ISPRUNING)) > - delnames[delnum++] = update->lock->ref_name; > + string_list_insert(&refs_to_delete, > + update->lock->ref_name); > } > } > > - if (repack_without_refs(delnames, delnum, err)) { > + if (repack_without_refs(&refs_to_delete, err)) { > ret = TRANSACTION_GENERIC_ERROR; > goto cleanup; > } > - for (i = 0; i < delnum; i++) > - unlink_or_warn(git_path("logs/%s", delnames[i])); > + for_each_string_list_item(ref_to_delete, &refs_to_delete) > + unlink_or_warn(git_path("logs/%s", ref_to_delete->string)); > clear_loose_ref_cache(&ref_cache); > > cleanup: > @@ -3833,7 +3832,7 @@ cleanup: > for (i = 0; i < n; i++) > if (updates[i]->lock) > unlock_ref(updates[i]->lock); > - free(delnames); > + string_list_clear(&refs_to_delete, 0); > return ret; > } > > diff --git a/refs.h b/refs.h > index 2bc3556..0416e5f 100644 > --- a/refs.h > +++ b/refs.h > @@ -163,8 +163,7 @@ extern void rollback_packed_refs(void); > */ > int pack_refs(unsigned int flags); > > -extern int repack_without_refs(const char **refnames, int n, > - struct strbuf *err); > +extern int repack_without_refs(struct string_list *without, struct strbuf *err); > > extern int ref_exists(const char *); ^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH] refs.c: use a stringlist for repack_without_refs 2014-11-18 23:06 ` Junio C Hamano @ 2014-11-18 23:39 ` Junio C Hamano 0 siblings, 0 replies; 61+ messages in thread From: Junio C Hamano @ 2014-11-18 23:39 UTC (permalink / raw) To: Stefan Beller; +Cc: git Junio C Hamano <gitster@pobox.com> writes: > Stefan Beller <sbeller@google.com> writes: > >> This patch was heavily inspired by a part of the ref-transactions-rename >> series[1], but people tend to dislike large series and this part is >> relatively easy to take out and unrelated, so I'll send it as a single >> patch. >> >> This patch doesn't intend any functional changes. It is just a refactoring, >> which replaces a char** array by a stringlist in the function >> repack_without_refs. >> >> [1] https://www.mail-archive.com/git@vger.kernel.org/msg60604.html >> >> Idea-by: Ronnie Sahlberg <sahlberg@google.com> >> Signed-off-by: Stefan Beller <sbeller@google.com> >> --- >> builtin/remote.c | 22 +++++++--------------- >> refs.c | 41 ++++++++++++++++++++--------------------- >> refs.h | 3 +-- >> 3 files changed, 28 insertions(+), 38 deletions(-) > > In one codepath we were already using a string_list delete_refs_list > anyway, so it makes sense to reuse that by movingan existing call to > string_list_insert() a bit higher, instead of maintaining another > array of pointers delete_refs[] to strings. > > OK, it simplifies the code by reducing the line count, which is a > plus ;-) > > Sounds good. I queued this but as I suspected yesterday had to drop all the other rs/ref-transaction-* topics that are not in 'next' yet. I am guessing that your plan is to make them come back one piece at a time in many easier-to-digest bite sized series. ^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH] refs.c: use a stringlist for repack_without_refs 2014-11-18 22:43 [PATCH] refs.c: use a stringlist for repack_without_refs Stefan Beller 2014-11-18 23:06 ` Junio C Hamano @ 2014-11-18 23:45 ` Jonathan Nieder 2014-11-19 0:28 ` Stefan Beller 2014-11-19 1:08 ` Stefan Beller 1 sibling, 2 replies; 61+ messages in thread From: Jonathan Nieder @ 2014-11-18 23:45 UTC (permalink / raw) To: Stefan Beller; +Cc: git, gitster, Ronnie Sahlberg Stefan Beller wrote: > This patch was heavily inspired by a part of the ref-transactions-rename > series[1], but people tend to dislike large series and this part is > relatively easy to take out and unrelated, so I'll send it as a single > patch. > > [1] https://www.mail-archive.com/git@vger.kernel.org/msg60604.html The above is a useful kind of comment to put below the three-dashes. It doesn't explain what the intent behind the patch is, why I should want this patch when considering whether to upgrade git, or what is going to break when I consider reverting it as part of fixing something else, so it doesn't belong in the commit message. > This patch doesn't intend any functional changes. It is just a refactoring, > which replaces a char** array by a stringlist in the function > repack_without_refs. Thanks. Why, though? Is it about having something simpler to pass from builtin/remote.c::remove_branches(), or something else? > Idea-by: Ronnie Sahlberg <sahlberg@google.com> > Signed-off-by: Stefan Beller <sbeller@google.com> Isn't the patch by Ronnie? Sometimes I send a patch by someone else and make some change that I don't want them to be blamed for. Then I keep their sign-off and put a note in the commit message about the change I made. See output from git log origin/pu --grep='jc:' for more examples of that. Some nits below. > --- a/builtin/remote.c > +++ b/builtin/remote.c [...] > @@ -1325,6 +1319,11 @@ static int prune_remote(const char *remote, int dry_run) [...] > memset(&states, 0, sizeof(states)); > get_remote_ref_states(remote, &states, GET_REF_STATES); > > + for (i = 0; i < states.stale.nr; i++) > + string_list_insert(&delete_refs_list, > + states.stale.items[i].util); > + > + > if (states.stale.nr) { (style) The double blank line looks odd here. > printf_ln(_("Pruning %s"), remote); > printf_ln(_("URL: %s"), > @@ -1332,24 +1331,17 @@ static int prune_remote(const char *remote, int dry_run) > ? states.remote->url[0] > : _("(no URL)")); > > - delete_refs = xmalloc(states.stale.nr * sizeof(*delete_refs)); Now that there's no delete_refs array duplicating the string list, would it make sense to rename delete_refs_list to delete_refs? As a nice side-effect, that would make the definition of delete_refs_list and other places it is used appear in the patch. > for (i = 0; i < states.stale.nr; i++) { > const char *refname = states.stale.items[i].util; (optional) this could be for_each_string_list_item(ref, &delete_refs_list) { const char *refname = ref->string; ... which saves the reader from having to remember what states.stale.items means. [...] > +++ b/refs.c [...] > @@ -2639,23 +2639,23 @@ int repack_without_refs(struct string_list *without, struct strbuf *err) [...] > - int i, ret, removed = 0; > + int count, ret, removed = 0; > > assert(err); > > - /* Look for a packed ref */ The old code has comments marking sections of the function: /* Look for a packed ref */ /* Avoid processing if we have nothing to do */ /* Remove refnames from the cache */ /* Remove any other accumulated cruft */ /* Write what remains */ Is dropping this comment intended? > - for (i = 0; i < n; i++) > - if (get_packed_ref(refnames[i])) > - break; > + count = 0; > + for_each_string_list_item(ref_to_delete, without) > + if (get_packed_ref(ref_to_delete->string)) > + count++; The old code breaks out early as soon as it finds a ref to delete. Can we do similar? E.g. for (i = 0; i < without->nr; i++) if (get_packed_ref(without->items[i].string)) break; (not about this patch) Is refs_to_delete leaked? [...] > @@ -3738,10 +3738,11 @@ static int ref_update_reject_duplicates(struct ref_update **updates, int n, > int ref_transaction_commit(struct ref_transaction *transaction, > struct strbuf *err) > { > - int ret = 0, delnum = 0, i; > - const char **delnames; > + int ret = 0, i; > int n = transaction->nr; > struct ref_update **updates = transaction->updates; > + struct string_list refs_to_delete = STRING_LIST_INIT_DUP; The old code doesn't xstrdup the list items, so _NODUP should work fine (and be slightly more efficient). [...] > @@ -3815,16 +3813,17 @@ int ref_transaction_commit(struct ref_transaction *transaction, > } > > if (!(update->flags & REF_ISPRUNING)) > - delnames[delnum++] = update->lock->ref_name; > + string_list_insert(&refs_to_delete, > + update->lock->ref_name); string_list_append would be analagous to the old code. [....] > --- a/refs.h > +++ b/refs.h > @@ -163,8 +163,7 @@ extern void rollback_packed_refs(void); > */ > int pack_refs(unsigned int flags); > > -extern int repack_without_refs(const char **refnames, int n, > - struct strbuf *err); > +extern int repack_without_refs(struct string_list *without, struct strbuf *err); A comment could mention whether the ref list needs to be sorted. (It doesn't, right?) Thanks and hope that helps, Jonathan ^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH] refs.c: use a stringlist for repack_without_refs 2014-11-18 23:45 ` Jonathan Nieder @ 2014-11-19 0:28 ` Stefan Beller 2014-11-19 1:08 ` Stefan Beller 1 sibling, 0 replies; 61+ messages in thread From: Stefan Beller @ 2014-11-19 0:28 UTC (permalink / raw) To: Jonathan Nieder; +Cc: git, Junio C Hamano, Ronnie Sahlberg On Tue, Nov 18, 2014 at 3:45 PM, Jonathan Nieder <jrnieder@gmail.com> wrote: > > The above is a useful kind of comment to put below the three-dashes. It > doesn't explain what the intent behind the patch is, why I should want > this patch when considering whether to upgrade git, or what is going to > break when I consider reverting it as part of fixing something else, so > it doesn't belong in the commit message. Yes, I'll do a resend, removing this paragraph. > >> This patch doesn't intend any functional changes. It is just a refactoring, >> which replaces a char** array by a stringlist in the function >> repack_without_refs. > > Thanks. Why, though? Is it about having something simpler to pass > from builtin/remote.c::remove_branches(), or something else? Essentially it's simpler to read and maintain as we're having less lines of code. I'll add that to the commit message instead. > >> Idea-by: Ronnie Sahlberg <sahlberg@google.com> >> Signed-off-by: Stefan Beller <sbeller@google.com> > > Isn't the patch by Ronnie? As it was part of the ref-transaction-rename series, it was authored by Ronnie. Porting it back to the master branch brought up so many conflicts, that I decided to rewrite it from scratch while having an occasional look at the original patch. If you want we can retain Ronnies authorship, however I may have messed up the rewriting, so I put my name as author and Ronnie as giving the idea. > > Sometimes I send a patch by someone else and make some change that I > don't want them to be blamed for. Then I keep their sign-off and put > a note in the commit message about the change I made. See output from Sounds reasonable, I can do something similar. > > git log origin/pu --grep='jc:' > > for more examples of that. > > Some nits below. Because of the nits, I'd rather be blamed. :) > >> --- a/builtin/remote.c >> +++ b/builtin/remote.c > [...] >> @@ -1325,6 +1319,11 @@ static int prune_remote(const char *remote, int dry_run) > [...] >> memset(&states, 0, sizeof(states)); >> get_remote_ref_states(remote, &states, GET_REF_STATES); >> >> + for (i = 0; i < states.stale.nr; i++) >> + string_list_insert(&delete_refs_list, >> + states.stale.items[i].util); >> + >> + >> if (states.stale.nr) { > > (style) The double blank line looks odd here. will fix > >> printf_ln(_("Pruning %s"), remote); >> printf_ln(_("URL: %s"), >> @@ -1332,24 +1331,17 @@ static int prune_remote(const char *remote, int dry_run) >> ? states.remote->url[0] >> : _("(no URL)")); >> >> - delete_refs = xmalloc(states.stale.nr * sizeof(*delete_refs)); > > Now that there's no delete_refs array duplicating the string list, > would it make sense to rename delete_refs_list to delete_refs? > > As a nice side-effect, that would make the definition of > delete_refs_list and other places it is used appear in the patch. > >> for (i = 0; i < states.stale.nr; i++) { >> const char *refname = states.stale.items[i].util; > > (optional) this could be > > for_each_string_list_item(ref, &delete_refs_list) { > const char *refname = ref->string; > ... > > which saves the reader from having to remember what states.stale.items > means. done > > [...] >> +++ b/refs.c > [...] >> @@ -2639,23 +2639,23 @@ int repack_without_refs(struct string_list *without, struct strbuf *err) > [...] >> - int i, ret, removed = 0; >> + int count, ret, removed = 0; >> >> assert(err); >> >> - /* Look for a packed ref */ > > The old code has comments marking sections of the function: > > /* Look for a packed ref */ > /* Avoid processing if we have nothing to do */ > /* Remove refnames from the cache */ > /* Remove any other accumulated cruft */ > /* Write what remains */ > > Is dropping this comment intended? no, dropped the dropping in the reroll. > >> - for (i = 0; i < n; i++) >> - if (get_packed_ref(refnames[i])) >> - break; >> + count = 0; >> + for_each_string_list_item(ref_to_delete, without) >> + if (get_packed_ref(ref_to_delete->string)) >> + count++; > > The old code breaks out early as soon as it finds a ref to delete. > Can we do similar? done > > E.g. > > for (i = 0; i < without->nr; i++) > if (get_packed_ref(without->items[i].string)) > break; > > (not about this patch) Is refs_to_delete leaked? > > [...] >> @@ -3738,10 +3738,11 @@ static int ref_update_reject_duplicates(struct ref_update **updates, int n, >> int ref_transaction_commit(struct ref_transaction *transaction, >> struct strbuf *err) >> { >> - int ret = 0, delnum = 0, i; >> - const char **delnames; >> + int ret = 0, i; >> int n = transaction->nr; >> struct ref_update **updates = transaction->updates; >> + struct string_list refs_to_delete = STRING_LIST_INIT_DUP; > > The old code doesn't xstrdup the list items, so _NODUP should work > fine (and be slightly more efficient). ok > > [...] >> @@ -3815,16 +3813,17 @@ int ref_transaction_commit(struct ref_transaction *transaction, >> } >> >> if (!(update->flags & REF_ISPRUNING)) >> - delnames[delnum++] = update->lock->ref_name; >> + string_list_insert(&refs_to_delete, >> + update->lock->ref_name); > > string_list_append would be analagous to the old code. ok > > [....] >> --- a/refs.h >> +++ b/refs.h >> @@ -163,8 +163,7 @@ extern void rollback_packed_refs(void); >> */ >> int pack_refs(unsigned int flags); >> >> -extern int repack_without_refs(const char **refnames, int n, >> - struct strbuf *err); >> +extern int repack_without_refs(struct string_list *without, struct strbuf *err); > > A comment could mention whether the ref list needs to be sorted. (It > doesn't, right?) ok, I tried adding comments. > > Thanks and hope that helps, > Jonathan ^ permalink raw reply [flat|nested] 61+ messages in thread
* [PATCH] refs.c: use a stringlist for repack_without_refs 2014-11-18 23:45 ` Jonathan Nieder 2014-11-19 0:28 ` Stefan Beller @ 2014-11-19 1:08 ` Stefan Beller 2014-11-19 18:00 ` Junio C Hamano 1 sibling, 1 reply; 61+ messages in thread From: Stefan Beller @ 2014-11-19 1:08 UTC (permalink / raw) To: sahlberg, gitster, git, jrnieder; +Cc: Stefan Beller This patch doesn't intend any functional changes. It is just a refactoring, which replaces a char** array by a stringlist in the function repack_without_refs. This is easier to read and maintain as it delivers the same functionality with less lines of code less pointers. Signed-off-by: Ronnie Sahlberg <sahlberg@google.com> Signed-off-by: Stefan Beller <sbeller@google.com> - This patch was heavily inspired by a part of the ref-transactions-rename series[1], but people tend to dislike large series and this part is relatively easy to take out and unrelated, so I'll send it as a single patch. [1] https://www.mail-archive.com/git@vger.kernel.org/msg60604.html --- builtin/remote.c | 31 +++++++++++-------------------- refs.c | 40 +++++++++++++++++++++------------------- refs.h | 10 ++++++++-- 3 files changed, 40 insertions(+), 41 deletions(-) diff --git a/builtin/remote.c b/builtin/remote.c index 7f28f92..5f5fa4c 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -750,16 +750,11 @@ static int mv(int argc, const char **argv) static int remove_branches(struct string_list *branches) { struct strbuf err = STRBUF_INIT; - const char **branch_names; int i, result = 0; - branch_names = xmalloc(branches->nr * sizeof(*branch_names)); - for (i = 0; i < branches->nr; i++) - branch_names[i] = branches->items[i].string; - if (repack_without_refs(branch_names, branches->nr, &err)) + if (repack_without_refs(branches, &err)) result |= error("%s", err.buf); strbuf_release(&err); - free(branch_names); for (i = 0; i < branches->nr; i++) { struct string_list_item *item = branches->items + i; @@ -1316,8 +1311,8 @@ static int prune_remote(const char *remote, int dry_run) { int result = 0, i; struct ref_states states; - struct string_list delete_refs_list = STRING_LIST_INIT_NODUP; - const char **delete_refs; + struct string_list delete_refs = STRING_LIST_INIT_NODUP; + struct string_list_item *ref; const char *dangling_msg = dry_run ? _(" %s will become dangling!") : _(" %s has become dangling!"); @@ -1325,6 +1320,9 @@ static int prune_remote(const char *remote, int dry_run) memset(&states, 0, sizeof(states)); get_remote_ref_states(remote, &states, GET_REF_STATES); + for_each_string_list_item(ref, &delete_refs) + string_list_append(&delete_refs, ref->string); + if (states.stale.nr) { printf_ln(_("Pruning %s"), remote); printf_ln(_("URL: %s"), @@ -1332,23 +1330,16 @@ static int prune_remote(const char *remote, int dry_run) ? states.remote->url[0] : _("(no URL)")); - delete_refs = xmalloc(states.stale.nr * sizeof(*delete_refs)); - for (i = 0; i < states.stale.nr; i++) - delete_refs[i] = states.stale.items[i].util; if (!dry_run) { struct strbuf err = STRBUF_INIT; - if (repack_without_refs(delete_refs, states.stale.nr, - &err)) + if (repack_without_refs(&delete_refs, &err)) result |= error("%s", err.buf); strbuf_release(&err); } - free(delete_refs); } - for (i = 0; i < states.stale.nr; i++) { - const char *refname = states.stale.items[i].util; - - string_list_insert(&delete_refs_list, refname); + for_each_string_list_item(ref, &delete_refs) { + const char *refname = ref->string; if (!dry_run) result |= delete_ref(refname, NULL, 0); @@ -1361,8 +1352,8 @@ static int prune_remote(const char *remote, int dry_run) abbrev_ref(refname, "refs/remotes/")); } - warn_dangling_symrefs(stdout, dangling_msg, &delete_refs_list); - string_list_clear(&delete_refs_list, 0); + warn_dangling_symrefs(stdout, dangling_msg, &delete_refs); + string_list_clear(&delete_refs, 0); free_remote_ref_states(&states); return result; diff --git a/refs.c b/refs.c index 5ff457e..2f6e08b 100644 --- a/refs.c +++ b/refs.c @@ -2639,23 +2639,26 @@ static int curate_packed_ref_fn(struct ref_entry *entry, void *cb_data) return 0; } -int repack_without_refs(const char **refnames, int n, struct strbuf *err) +int repack_without_refs(struct string_list *without, struct strbuf *err) { struct ref_dir *packed; struct string_list refs_to_delete = STRING_LIST_INIT_DUP; struct string_list_item *ref_to_delete; - int i, ret, removed = 0; + int ret, needs_repacking = 0, removed = 0; assert(err); /* Look for a packed ref */ - for (i = 0; i < n; i++) - if (get_packed_ref(refnames[i])) + for_each_string_list_item(ref_to_delete, without) { + if (get_packed_ref(ref_to_delete->string)) { + needs_repacking = 1; break; + } + } - /* Avoid locking if we have nothing to do */ - if (i == n) - return 0; /* no refname exists in packed refs */ + /* No refname exists in packed refs */ + if (!needs_repacking) + return 0; if (lock_packed_refs(0)) { unable_to_lock_message(git_path("packed-refs"), errno, err); @@ -2664,8 +2667,8 @@ int repack_without_refs(const char **refnames, int n, struct strbuf *err) packed = get_packed_refs(&ref_cache); /* Remove refnames from the cache */ - for (i = 0; i < n; i++) - if (remove_entry(packed, refnames[i]) != -1) + for_each_string_list_item(ref_to_delete, without) + if (remove_entry(packed, ref_to_delete->string) != -1) removed = 1; if (!removed) { /* @@ -3738,10 +3741,11 @@ static int ref_update_reject_duplicates(struct ref_update **updates, int n, int ref_transaction_commit(struct ref_transaction *transaction, struct strbuf *err) { - int ret = 0, delnum = 0, i; - const char **delnames; + int ret = 0, i; int n = transaction->nr; struct ref_update **updates = transaction->updates; + struct string_list refs_to_delete = STRING_LIST_INIT_NODUP; + struct string_list_item *ref_to_delete; assert(err); @@ -3753,9 +3757,6 @@ int ref_transaction_commit(struct ref_transaction *transaction, return 0; } - /* Allocate work space */ - delnames = xmalloc(sizeof(*delnames) * n); - /* Copy, sort, and reject duplicate refs */ qsort(updates, n, sizeof(*updates), ref_update_compare); if (ref_update_reject_duplicates(updates, n, err)) { @@ -3815,16 +3816,17 @@ int ref_transaction_commit(struct ref_transaction *transaction, } if (!(update->flags & REF_ISPRUNING)) - delnames[delnum++] = update->lock->ref_name; + string_list_append(&refs_to_delete, + update->lock->ref_name); } } - if (repack_without_refs(delnames, delnum, err)) { + if (repack_without_refs(&refs_to_delete, err)) { ret = TRANSACTION_GENERIC_ERROR; goto cleanup; } - for (i = 0; i < delnum; i++) - unlink_or_warn(git_path("logs/%s", delnames[i])); + for_each_string_list_item(ref_to_delete, &refs_to_delete) + unlink_or_warn(git_path("logs/%s", ref_to_delete->string)); clear_loose_ref_cache(&ref_cache); cleanup: @@ -3833,7 +3835,7 @@ cleanup: for (i = 0; i < n; i++) if (updates[i]->lock) unlock_ref(updates[i]->lock); - free(delnames); + string_list_clear(&refs_to_delete, 0); return ret; } diff --git a/refs.h b/refs.h index 2bc3556..69f88ef 100644 --- a/refs.h +++ b/refs.h @@ -163,8 +163,14 @@ extern void rollback_packed_refs(void); */ int pack_refs(unsigned int flags); -extern int repack_without_refs(const char **refnames, int n, - struct strbuf *err); +/* + * Repacks the refs pack file excluding the refs given + * without: The refs to be excluded from the new refs pack file, + * May be unsorted + * err: String buffer, which will be used for reporting errors, + * Must not be NULL + */ +extern int repack_without_refs(struct string_list *without, struct strbuf *err); extern int ref_exists(const char *); -- 2.2.0.rc2.5.gf7b9fb2 ^ permalink raw reply related [flat|nested] 61+ messages in thread
* Re: [PATCH] refs.c: use a stringlist for repack_without_refs 2014-11-19 1:08 ` Stefan Beller @ 2014-11-19 18:00 ` Junio C Hamano 2014-11-19 18:50 ` Stefan Beller 0 siblings, 1 reply; 61+ messages in thread From: Junio C Hamano @ 2014-11-19 18:00 UTC (permalink / raw) To: Stefan Beller; +Cc: git Stefan Beller <sbeller@google.com> writes: > This patch doesn't intend any functional changes. It is just > a refactoring, which replaces a char** array by a stringlist > in the function repack_without_refs. > This is easier to read and maintain as it delivers the same > functionality with less lines of code less pointers. > > Signed-off-by: Ronnie Sahlberg <sahlberg@google.com> > Signed-off-by: Stefan Beller <sbeller@google.com> > > - Have three of them, not just one, here. (no need to resend to fix only this). Or... > > This patch was heavily inspired by a part of the ref-transactions-rename > series[1], but people tend to dislike large series and this part is > relatively easy to take out and unrelated, so I'll send it as a single > patch. > > [1] https://www.mail-archive.com/git@vger.kernel.org/msg60604.html > > --- ... next time, write the comments here, where Git already gives you three dashes. Also mention what you updated and why relative to your earlier round here, if not covered in the log message already. For example, renaming of delete_refs_list (in v1) to delete_refs (this version) is a sensible change because readers know it is a list from its type being string_list already, but that change is new relative to the codebase, so it could go to the log message ("Having array delete_refs[] and string_list delete_refs_list is redundant; drop the array and give the string_list variable the shorter name", or something like that) if you wanted to. > @@ -1316,8 +1311,8 @@ static int prune_remote(const char *remote, int dry_run) > { > int result = 0, i; > struct ref_states states; > - struct string_list delete_refs_list = STRING_LIST_INIT_NODUP; > - const char **delete_refs; > + struct string_list delete_refs = STRING_LIST_INIT_NODUP; > + struct string_list_item *ref; > const char *dangling_msg = dry_run > ? _(" %s will become dangling!") > : _(" %s has become dangling!"); > @@ -1325,6 +1320,9 @@ static int prune_remote(const char *remote, int dry_run) > memset(&states, 0, sizeof(states)); > get_remote_ref_states(remote, &states, GET_REF_STATES); > > + for_each_string_list_item(ref, &delete_refs) > + string_list_append(&delete_refs, ref->string); What are you trying to do here? Initialise delete_refs to an empty string list, and then iterate over its elements and append them into the same string list??? It looks like a "currently noop, waiting for somebody to throw an item to the list before this code, at which time it turns into an infinite memory eater". Curious... ^ permalink raw reply [flat|nested] 61+ messages in thread
* [PATCH] refs.c: use a stringlist for repack_without_refs 2014-11-19 18:00 ` Junio C Hamano @ 2014-11-19 18:50 ` Stefan Beller 2014-11-19 20:44 ` Jonathan Nieder 2014-11-21 14:09 ` [PATCH 0/6] repack_without_refs(): convert to string_list Michael Haggerty 0 siblings, 2 replies; 61+ messages in thread From: Stefan Beller @ 2014-11-19 18:50 UTC (permalink / raw) To: gitster, sahlberg, jrnieder, git; +Cc: Stefan Beller From: Ronnie Sahlberg <sahlberg@google.com> This patch doesn't intend any functional changes. It is just a refactoring, which replaces a char** array by a stringlist in the function repack_without_refs. This is easier to read and maintain as it delivers the same functionality with less lines of code and less pointers. [sb: ported this patch from a larger patch series to the master branch, added documentary comments in refs.h] Signed-off-by: Ronnie Sahlberg <sahlberg@google.com> Signed-off-by: Stefan Beller <sbeller@google.com> --- On Wed, Nov 19, 2014 at 10:00 AM, Junio C Hamano <gitster@pobox.com> wrote: > + for_each_string_list_item(ref, &delete_refs) > + string_list_append(&delete_refs, ref->string); > What are you trying to do here? I messed up this patch completely yesterday in the evening. Essentially the inter-patch diff are all the nits by Jonathan. So here is my attempt on sending a more maintainer friendly patch. Changes to version 1: * removed the double blank line * rename delete_refs_list to delete_refs * add back comments dropped by accident * use STRING_LIST_INIT_NODUP instead of the _DUP version in ref_transaction_commit * add documentary comments on the repack_without_refs function * user string_list_append instead of string_list_insert as it follows the previous behavior more closely. * put back the early exit of the loop in repack_without_refs Changes to version 2: * fixed commit message (comments after the three dashes) * fixed the curiosity Junio pointed out as it was just wrong code. Now it actually builds a list of all states.stale.items[i].util items. builtin/remote.c | 31 +++++++++++-------------------- refs.c | 40 +++++++++++++++++++++------------------- refs.h | 10 ++++++++-- 3 files changed, 40 insertions(+), 41 deletions(-) diff --git a/builtin/remote.c b/builtin/remote.c index 7f28f92..0d89aba 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -750,16 +750,11 @@ static int mv(int argc, const char **argv) static int remove_branches(struct string_list *branches) { struct strbuf err = STRBUF_INIT; - const char **branch_names; int i, result = 0; - branch_names = xmalloc(branches->nr * sizeof(*branch_names)); - for (i = 0; i < branches->nr; i++) - branch_names[i] = branches->items[i].string; - if (repack_without_refs(branch_names, branches->nr, &err)) + if (repack_without_refs(branches, &err)) result |= error("%s", err.buf); strbuf_release(&err); - free(branch_names); for (i = 0; i < branches->nr; i++) { struct string_list_item *item = branches->items + i; @@ -1316,8 +1311,8 @@ static int prune_remote(const char *remote, int dry_run) { int result = 0, i; struct ref_states states; - struct string_list delete_refs_list = STRING_LIST_INIT_NODUP; - const char **delete_refs; + struct string_list delete_refs = STRING_LIST_INIT_NODUP; + struct string_list_item *ref; const char *dangling_msg = dry_run ? _(" %s will become dangling!") : _(" %s has become dangling!"); @@ -1325,6 +1320,9 @@ static int prune_remote(const char *remote, int dry_run) memset(&states, 0, sizeof(states)); get_remote_ref_states(remote, &states, GET_REF_STATES); + for (i = 0; i < states.stale.nr; i++) + string_list_append(&delete_refs, states.stale.items[i].util); + if (states.stale.nr) { printf_ln(_("Pruning %s"), remote); printf_ln(_("URL: %s"), @@ -1332,23 +1330,16 @@ static int prune_remote(const char *remote, int dry_run) ? states.remote->url[0] : _("(no URL)")); - delete_refs = xmalloc(states.stale.nr * sizeof(*delete_refs)); - for (i = 0; i < states.stale.nr; i++) - delete_refs[i] = states.stale.items[i].util; if (!dry_run) { struct strbuf err = STRBUF_INIT; - if (repack_without_refs(delete_refs, states.stale.nr, - &err)) + if (repack_without_refs(&delete_refs, &err)) result |= error("%s", err.buf); strbuf_release(&err); } - free(delete_refs); } - for (i = 0; i < states.stale.nr; i++) { - const char *refname = states.stale.items[i].util; - - string_list_insert(&delete_refs_list, refname); + for_each_string_list_item(ref, &delete_refs) { + const char *refname = ref->string; if (!dry_run) result |= delete_ref(refname, NULL, 0); @@ -1361,8 +1352,8 @@ static int prune_remote(const char *remote, int dry_run) abbrev_ref(refname, "refs/remotes/")); } - warn_dangling_symrefs(stdout, dangling_msg, &delete_refs_list); - string_list_clear(&delete_refs_list, 0); + warn_dangling_symrefs(stdout, dangling_msg, &delete_refs); + string_list_clear(&delete_refs, 0); free_remote_ref_states(&states); return result; diff --git a/refs.c b/refs.c index 5ff457e..2f6e08b 100644 --- a/refs.c +++ b/refs.c @@ -2639,23 +2639,26 @@ static int curate_packed_ref_fn(struct ref_entry *entry, void *cb_data) return 0; } -int repack_without_refs(const char **refnames, int n, struct strbuf *err) +int repack_without_refs(struct string_list *without, struct strbuf *err) { struct ref_dir *packed; struct string_list refs_to_delete = STRING_LIST_INIT_DUP; struct string_list_item *ref_to_delete; - int i, ret, removed = 0; + int ret, needs_repacking = 0, removed = 0; assert(err); /* Look for a packed ref */ - for (i = 0; i < n; i++) - if (get_packed_ref(refnames[i])) + for_each_string_list_item(ref_to_delete, without) { + if (get_packed_ref(ref_to_delete->string)) { + needs_repacking = 1; break; + } + } - /* Avoid locking if we have nothing to do */ - if (i == n) - return 0; /* no refname exists in packed refs */ + /* No refname exists in packed refs */ + if (!needs_repacking) + return 0; if (lock_packed_refs(0)) { unable_to_lock_message(git_path("packed-refs"), errno, err); @@ -2664,8 +2667,8 @@ int repack_without_refs(const char **refnames, int n, struct strbuf *err) packed = get_packed_refs(&ref_cache); /* Remove refnames from the cache */ - for (i = 0; i < n; i++) - if (remove_entry(packed, refnames[i]) != -1) + for_each_string_list_item(ref_to_delete, without) + if (remove_entry(packed, ref_to_delete->string) != -1) removed = 1; if (!removed) { /* @@ -3738,10 +3741,11 @@ static int ref_update_reject_duplicates(struct ref_update **updates, int n, int ref_transaction_commit(struct ref_transaction *transaction, struct strbuf *err) { - int ret = 0, delnum = 0, i; - const char **delnames; + int ret = 0, i; int n = transaction->nr; struct ref_update **updates = transaction->updates; + struct string_list refs_to_delete = STRING_LIST_INIT_NODUP; + struct string_list_item *ref_to_delete; assert(err); @@ -3753,9 +3757,6 @@ int ref_transaction_commit(struct ref_transaction *transaction, return 0; } - /* Allocate work space */ - delnames = xmalloc(sizeof(*delnames) * n); - /* Copy, sort, and reject duplicate refs */ qsort(updates, n, sizeof(*updates), ref_update_compare); if (ref_update_reject_duplicates(updates, n, err)) { @@ -3815,16 +3816,17 @@ int ref_transaction_commit(struct ref_transaction *transaction, } if (!(update->flags & REF_ISPRUNING)) - delnames[delnum++] = update->lock->ref_name; + string_list_append(&refs_to_delete, + update->lock->ref_name); } } - if (repack_without_refs(delnames, delnum, err)) { + if (repack_without_refs(&refs_to_delete, err)) { ret = TRANSACTION_GENERIC_ERROR; goto cleanup; } - for (i = 0; i < delnum; i++) - unlink_or_warn(git_path("logs/%s", delnames[i])); + for_each_string_list_item(ref_to_delete, &refs_to_delete) + unlink_or_warn(git_path("logs/%s", ref_to_delete->string)); clear_loose_ref_cache(&ref_cache); cleanup: @@ -3833,7 +3835,7 @@ cleanup: for (i = 0; i < n; i++) if (updates[i]->lock) unlock_ref(updates[i]->lock); - free(delnames); + string_list_clear(&refs_to_delete, 0); return ret; } diff --git a/refs.h b/refs.h index 2bc3556..69f88ef 100644 --- a/refs.h +++ b/refs.h @@ -163,8 +163,14 @@ extern void rollback_packed_refs(void); */ int pack_refs(unsigned int flags); -extern int repack_without_refs(const char **refnames, int n, - struct strbuf *err); +/* + * Repacks the refs pack file excluding the refs given + * without: The refs to be excluded from the new refs pack file, + * May be unsorted + * err: String buffer, which will be used for reporting errors, + * Must not be NULL + */ +extern int repack_without_refs(struct string_list *without, struct strbuf *err); extern int ref_exists(const char *); -- 2.2.0.rc2.13.g0786cdb ^ permalink raw reply related [flat|nested] 61+ messages in thread
* Re: [PATCH] refs.c: use a stringlist for repack_without_refs 2014-11-19 18:50 ` Stefan Beller @ 2014-11-19 20:44 ` Jonathan Nieder 2014-11-19 21:54 ` Stefan Beller 2014-11-21 14:09 ` [PATCH 0/6] repack_without_refs(): convert to string_list Michael Haggerty 1 sibling, 1 reply; 61+ messages in thread From: Jonathan Nieder @ 2014-11-19 20:44 UTC (permalink / raw) To: Stefan Beller; +Cc: gitster, sahlberg, git Stefan Beller wrote: > This patch doesn't intend any functional changes. Yay. :) > a refactoring, which replaces a char** array by a stringlist > in the function repack_without_refs. > This is easier to read and maintain as it delivers the same > functionality with less lines of code and less pointers. Please wrap to a consistent width and add a blank line between paragraphs. So, either: ... repack_without_refs. This is easier to read and ... or: ... repack_without_refs. This is easier to read and ... [...] > +++ b/builtin/remote.c > @@ -750,16 +750,11 @@ static int mv(int argc, const char **argv) [...] > @@ -1325,6 +1320,9 @@ static int prune_remote(const char *remote, int dry_run) > memset(&states, 0, sizeof(states)); > get_remote_ref_states(remote, &states, GET_REF_STATES); > > + for (i = 0; i < states.stale.nr; i++) > + string_list_append(&delete_refs, states.stale.items[i].util); warn_dangling_symref requires a sorted list. Possible fixes: (a) switch to string_list_insert, or (b) [nicer] call sort_string_list before the warn_dangling_symrefs call. [...] > --- a/refs.c > +++ b/refs.c > @@ -2639,23 +2639,26 @@ static int curate_packed_ref_fn(struct ref_entry *entry, void *cb_data) > return 0; > } > > -int repack_without_refs(const char **refnames, int n, struct strbuf *err) > +int repack_without_refs(struct string_list *without, struct strbuf *err) > { > struct ref_dir *packed; > struct string_list refs_to_delete = STRING_LIST_INIT_DUP; > struct string_list_item *ref_to_delete; > - int i, ret, removed = 0; > + int ret, needs_repacking = 0, removed = 0; > > assert(err); > > /* Look for a packed ref */ > - for (i = 0; i < n; i++) > - if (get_packed_ref(refnames[i])) > + for_each_string_list_item(ref_to_delete, without) { > + if (get_packed_ref(ref_to_delete->string)) { > + needs_repacking = 1; > break; > + } > + } > > - /* Avoid locking if we have nothing to do */ This comment was helpful --- it's sad to lose it (but if you feel strongly about it then I don't mind). > - if (i == n) > - return 0; /* no refname exists in packed refs */ > + /* No refname exists in packed refs */ > + if (!needs_repacking) > + return 0; I kind of liked the 'i == n' test that avoided needing a new auxiliary variable. This is fine and probably a little clearer, though. [...] > --- a/refs.h > +++ b/refs.h > @@ -163,8 +163,14 @@ extern void rollback_packed_refs(void); > */ > int pack_refs(unsigned int flags); > > -extern int repack_without_refs(const char **refnames, int n, > - struct strbuf *err); > +/* > + * Repacks the refs pack file excluding the refs given > + * without: The refs to be excluded from the new refs pack file, > + * May be unsorted > + * err: String buffer, which will be used for reporting errors, > + * Must not be NULL > + */ > +extern int repack_without_refs(struct string_list *without, struct strbuf *err); (nit) Other comments in this file use the imperative mood to describe what a function does, so it would be a little clearer to do that here, too ("Repack the ..." instead of "Repacks the ..."). It might be just me, but I find this formatted comment with everything jammed together hard to read. I'd prefer a simple paragraph, like: /* * Remove the refs listed in 'without' from the packed-refs file. * On error, packed-refs will be unchanged, the return value is * nonzero, and a message about the error is written to the 'err' * strbuf. */ Thanks, Jonathan ^ permalink raw reply [flat|nested] 61+ messages in thread
* [PATCH] refs.c: use a stringlist for repack_without_refs 2014-11-19 20:44 ` Jonathan Nieder @ 2014-11-19 21:54 ` Stefan Beller 2014-11-19 21:59 ` [PATCH v4] " Stefan Beller 0 siblings, 1 reply; 61+ messages in thread From: Stefan Beller @ 2014-11-19 21:54 UTC (permalink / raw) To: gitster, sahlberg, git, jrnieder; +Cc: Stefan Beller From: Ronnie Sahlberg <sahlberg@google.com> This patch doesn't intend any functional changes. It is just a refactoring, which replaces a char** array by a stringlist in the function repack_without_refs. This is easier to read and maintain as it delivers the same functionality with less lines of code and less pointers. [sb: ported this patch from a larger patch series to the master branch, added documentary comments in refs.h] Signed-off-by: Ronnie Sahlberg <sahlberg@google.com> Signed-off-by: Stefan Beller <sbeller@google.com> --- version 3 includes all nits by Jonathan. Changes to version 1: * removed the double blank line * rename delete_refs_list to delete_refs * add back comments dropped by accident * use STRING_LIST_INIT_NODUP instead of the _DUP version in ref_transaction_commit * add documentary comments on the repack_without_refs function * user string_list_append instead of string_list_insert as it follows the previous behavior more closely. * put back the early exit of the loop in repack_without_refs Changes to version 2: * fixed commit message (comments after the three dashes) * fixed the curiosity Junio pointed out as it was just wrong code. Now it actually builds a list of all states.stale.items[i].util items. Changes in version 3: * reword commit message * sort delete_refs before passing it to warn_dangling_symrefs * change the comments (get back the one jrn complained about) in repack_without_refs * use the suggestion of jonathan for documenting repack_without_refs in the header. Add a note about the arguments. --- builtin/remote.c | 32 ++++++++++++-------------------- refs.c | 38 ++++++++++++++++++++------------------ refs.h | 10 ++++++++-- 3 files changed, 40 insertions(+), 40 deletions(-) diff --git a/builtin/remote.c b/builtin/remote.c index 7f28f92..b37ed3d 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -750,16 +750,11 @@ static int mv(int argc, const char **argv) static int remove_branches(struct string_list *branches) { struct strbuf err = STRBUF_INIT; - const char **branch_names; int i, result = 0; - branch_names = xmalloc(branches->nr * sizeof(*branch_names)); - for (i = 0; i < branches->nr; i++) - branch_names[i] = branches->items[i].string; - if (repack_without_refs(branch_names, branches->nr, &err)) + if (repack_without_refs(branches, &err)) result |= error("%s", err.buf); strbuf_release(&err); - free(branch_names); for (i = 0; i < branches->nr; i++) { struct string_list_item *item = branches->items + i; @@ -1316,8 +1311,8 @@ static int prune_remote(const char *remote, int dry_run) { int result = 0, i; struct ref_states states; - struct string_list delete_refs_list = STRING_LIST_INIT_NODUP; - const char **delete_refs; + struct string_list delete_refs = STRING_LIST_INIT_NODUP; + struct string_list_item *ref; const char *dangling_msg = dry_run ? _(" %s will become dangling!") : _(" %s has become dangling!"); @@ -1325,6 +1320,9 @@ static int prune_remote(const char *remote, int dry_run) memset(&states, 0, sizeof(states)); get_remote_ref_states(remote, &states, GET_REF_STATES); + for (i = 0; i < states.stale.nr; i++) + string_list_append(&delete_refs, states.stale.items[i].util); + if (states.stale.nr) { printf_ln(_("Pruning %s"), remote); printf_ln(_("URL: %s"), @@ -1332,23 +1330,16 @@ static int prune_remote(const char *remote, int dry_run) ? states.remote->url[0] : _("(no URL)")); - delete_refs = xmalloc(states.stale.nr * sizeof(*delete_refs)); - for (i = 0; i < states.stale.nr; i++) - delete_refs[i] = states.stale.items[i].util; if (!dry_run) { struct strbuf err = STRBUF_INIT; - if (repack_without_refs(delete_refs, states.stale.nr, - &err)) + if (repack_without_refs(&delete_refs, &err)) result |= error("%s", err.buf); strbuf_release(&err); } - free(delete_refs); } - for (i = 0; i < states.stale.nr; i++) { - const char *refname = states.stale.items[i].util; - - string_list_insert(&delete_refs_list, refname); + for_each_string_list_item(ref, &delete_refs) { + const char *refname = ref->string; if (!dry_run) result |= delete_ref(refname, NULL, 0); @@ -1361,8 +1352,9 @@ static int prune_remote(const char *remote, int dry_run) abbrev_ref(refname, "refs/remotes/")); } - warn_dangling_symrefs(stdout, dangling_msg, &delete_refs_list); - string_list_clear(&delete_refs_list, 0); + sort_string_list(&delete_refs); + warn_dangling_symrefs(stdout, dangling_msg, &delete_refs); + string_list_clear(&delete_refs, 0); free_remote_ref_states(&states); return result; diff --git a/refs.c b/refs.c index 5ff457e..ebcd90f 100644 --- a/refs.c +++ b/refs.c @@ -2639,23 +2639,26 @@ static int curate_packed_ref_fn(struct ref_entry *entry, void *cb_data) return 0; } -int repack_without_refs(const char **refnames, int n, struct strbuf *err) +int repack_without_refs(struct string_list *without, struct strbuf *err) { struct ref_dir *packed; struct string_list refs_to_delete = STRING_LIST_INIT_DUP; struct string_list_item *ref_to_delete; - int i, ret, removed = 0; + int ret, needs_repacking = 0, removed = 0; assert(err); /* Look for a packed ref */ - for (i = 0; i < n; i++) - if (get_packed_ref(refnames[i])) + for_each_string_list_item(ref_to_delete, without) { + if (get_packed_ref(ref_to_delete->string)) { + needs_repacking = 1; break; + } + } /* Avoid locking if we have nothing to do */ - if (i == n) - return 0; /* no refname exists in packed refs */ + if (!needs_repacking) + return 0; if (lock_packed_refs(0)) { unable_to_lock_message(git_path("packed-refs"), errno, err); @@ -2664,8 +2667,8 @@ int repack_without_refs(const char **refnames, int n, struct strbuf *err) packed = get_packed_refs(&ref_cache); /* Remove refnames from the cache */ - for (i = 0; i < n; i++) - if (remove_entry(packed, refnames[i]) != -1) + for_each_string_list_item(ref_to_delete, without) + if (remove_entry(packed, ref_to_delete->string) != -1) removed = 1; if (!removed) { /* @@ -3738,10 +3741,11 @@ static int ref_update_reject_duplicates(struct ref_update **updates, int n, int ref_transaction_commit(struct ref_transaction *transaction, struct strbuf *err) { - int ret = 0, delnum = 0, i; - const char **delnames; + int ret = 0, i; int n = transaction->nr; struct ref_update **updates = transaction->updates; + struct string_list refs_to_delete = STRING_LIST_INIT_NODUP; + struct string_list_item *ref_to_delete; assert(err); @@ -3753,9 +3757,6 @@ int ref_transaction_commit(struct ref_transaction *transaction, return 0; } - /* Allocate work space */ - delnames = xmalloc(sizeof(*delnames) * n); - /* Copy, sort, and reject duplicate refs */ qsort(updates, n, sizeof(*updates), ref_update_compare); if (ref_update_reject_duplicates(updates, n, err)) { @@ -3815,16 +3816,17 @@ int ref_transaction_commit(struct ref_transaction *transaction, } if (!(update->flags & REF_ISPRUNING)) - delnames[delnum++] = update->lock->ref_name; + string_list_append(&refs_to_delete, + update->lock->ref_name); } } - if (repack_without_refs(delnames, delnum, err)) { + if (repack_without_refs(&refs_to_delete, err)) { ret = TRANSACTION_GENERIC_ERROR; goto cleanup; } - for (i = 0; i < delnum; i++) - unlink_or_warn(git_path("logs/%s", delnames[i])); + for_each_string_list_item(ref_to_delete, &refs_to_delete) + unlink_or_warn(git_path("logs/%s", ref_to_delete->string)); clear_loose_ref_cache(&ref_cache); cleanup: @@ -3833,7 +3835,7 @@ cleanup: for (i = 0; i < n; i++) if (updates[i]->lock) unlock_ref(updates[i]->lock); - free(delnames); + string_list_clear(&refs_to_delete, 0); return ret; } diff --git a/refs.h b/refs.h index 2bc3556..69f88ef 100644 --- a/refs.h +++ b/refs.h @@ -163,8 +163,14 @@ extern void rollback_packed_refs(void); */ int pack_refs(unsigned int flags); -extern int repack_without_refs(const char **refnames, int n, - struct strbuf *err); +/* + * Repacks the refs pack file excluding the refs given + * without: The refs to be excluded from the new refs pack file, + * May be unsorted + * err: String buffer, which will be used for reporting errors, + * Must not be NULL + */ +extern int repack_without_refs(struct string_list *without, struct strbuf *err); extern int ref_exists(const char *); -- 2.2.0.rc2.13.g0786cdb ^ permalink raw reply related [flat|nested] 61+ messages in thread
* [PATCH v4] refs.c: use a stringlist for repack_without_refs 2014-11-19 21:54 ` Stefan Beller @ 2014-11-19 21:59 ` Stefan Beller 2014-11-20 2:15 ` Jonathan Nieder 0 siblings, 1 reply; 61+ messages in thread From: Stefan Beller @ 2014-11-19 21:59 UTC (permalink / raw) To: gitster, sahlberg, git, jrnieder; +Cc: Stefan Beller From: Ronnie Sahlberg <sahlberg@google.com> This patch doesn't intend any functional changes. It is just a refactoring, which replaces a char** array by a stringlist in the function repack_without_refs. This is easier to read and maintain as it delivers the same functionality with less lines of code and less pointers. [sb: ported this patch from a larger patch series to the master branch, added documentary comments in refs.h] Signed-off-by: Ronnie Sahlberg <sahlberg@google.com> Signed-off-by: Stefan Beller <sbeller@google.com> --- Changes to version 1: * removed the double blank line * rename delete_refs_list to delete_refs * add back comments dropped by accident * use STRING_LIST_INIT_NODUP instead of the _DUP version in ref_transaction_commit * add documentary comments on the repack_without_refs function * user string_list_append instead of string_list_insert as it follows the previous behavior more closely. * put back the early exit of the loop in repack_without_refs Changes to version 2: * fixed commit message (comments after the three dashes) * fixed the curiosity Junio pointed out as it was just wrong code. Now it actually builds a list of all states.stale.items[i].util items. Changes in version 3: * reword commit message * sort delete_refs before passing it to warn_dangling_symrefs * change the comments (get back the one jrn complained about) in repack_without_refs * use the suggestion of jonathan for documenting repack_without_refs in the header. Add a note about the arguments. Changes in version 4: * I lied, when saying I had all the nits from Jonathan. I messed up the documentation in the header. This includes the documentary comment in the header. --- builtin/remote.c | 32 ++++++++++++-------------------- refs.c | 38 ++++++++++++++++++++------------------ refs.h | 11 +++++++++-- 3 files changed, 41 insertions(+), 40 deletions(-) diff --git a/builtin/remote.c b/builtin/remote.c index 7f28f92..b37ed3d 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -750,16 +750,11 @@ static int mv(int argc, const char **argv) static int remove_branches(struct string_list *branches) { struct strbuf err = STRBUF_INIT; - const char **branch_names; int i, result = 0; - branch_names = xmalloc(branches->nr * sizeof(*branch_names)); - for (i = 0; i < branches->nr; i++) - branch_names[i] = branches->items[i].string; - if (repack_without_refs(branch_names, branches->nr, &err)) + if (repack_without_refs(branches, &err)) result |= error("%s", err.buf); strbuf_release(&err); - free(branch_names); for (i = 0; i < branches->nr; i++) { struct string_list_item *item = branches->items + i; @@ -1316,8 +1311,8 @@ static int prune_remote(const char *remote, int dry_run) { int result = 0, i; struct ref_states states; - struct string_list delete_refs_list = STRING_LIST_INIT_NODUP; - const char **delete_refs; + struct string_list delete_refs = STRING_LIST_INIT_NODUP; + struct string_list_item *ref; const char *dangling_msg = dry_run ? _(" %s will become dangling!") : _(" %s has become dangling!"); @@ -1325,6 +1320,9 @@ static int prune_remote(const char *remote, int dry_run) memset(&states, 0, sizeof(states)); get_remote_ref_states(remote, &states, GET_REF_STATES); + for (i = 0; i < states.stale.nr; i++) + string_list_append(&delete_refs, states.stale.items[i].util); + if (states.stale.nr) { printf_ln(_("Pruning %s"), remote); printf_ln(_("URL: %s"), @@ -1332,23 +1330,16 @@ static int prune_remote(const char *remote, int dry_run) ? states.remote->url[0] : _("(no URL)")); - delete_refs = xmalloc(states.stale.nr * sizeof(*delete_refs)); - for (i = 0; i < states.stale.nr; i++) - delete_refs[i] = states.stale.items[i].util; if (!dry_run) { struct strbuf err = STRBUF_INIT; - if (repack_without_refs(delete_refs, states.stale.nr, - &err)) + if (repack_without_refs(&delete_refs, &err)) result |= error("%s", err.buf); strbuf_release(&err); } - free(delete_refs); } - for (i = 0; i < states.stale.nr; i++) { - const char *refname = states.stale.items[i].util; - - string_list_insert(&delete_refs_list, refname); + for_each_string_list_item(ref, &delete_refs) { + const char *refname = ref->string; if (!dry_run) result |= delete_ref(refname, NULL, 0); @@ -1361,8 +1352,9 @@ static int prune_remote(const char *remote, int dry_run) abbrev_ref(refname, "refs/remotes/")); } - warn_dangling_symrefs(stdout, dangling_msg, &delete_refs_list); - string_list_clear(&delete_refs_list, 0); + sort_string_list(&delete_refs); + warn_dangling_symrefs(stdout, dangling_msg, &delete_refs); + string_list_clear(&delete_refs, 0); free_remote_ref_states(&states); return result; diff --git a/refs.c b/refs.c index 5ff457e..ebcd90f 100644 --- a/refs.c +++ b/refs.c @@ -2639,23 +2639,26 @@ static int curate_packed_ref_fn(struct ref_entry *entry, void *cb_data) return 0; } -int repack_without_refs(const char **refnames, int n, struct strbuf *err) +int repack_without_refs(struct string_list *without, struct strbuf *err) { struct ref_dir *packed; struct string_list refs_to_delete = STRING_LIST_INIT_DUP; struct string_list_item *ref_to_delete; - int i, ret, removed = 0; + int ret, needs_repacking = 0, removed = 0; assert(err); /* Look for a packed ref */ - for (i = 0; i < n; i++) - if (get_packed_ref(refnames[i])) + for_each_string_list_item(ref_to_delete, without) { + if (get_packed_ref(ref_to_delete->string)) { + needs_repacking = 1; break; + } + } /* Avoid locking if we have nothing to do */ - if (i == n) - return 0; /* no refname exists in packed refs */ + if (!needs_repacking) + return 0; if (lock_packed_refs(0)) { unable_to_lock_message(git_path("packed-refs"), errno, err); @@ -2664,8 +2667,8 @@ int repack_without_refs(const char **refnames, int n, struct strbuf *err) packed = get_packed_refs(&ref_cache); /* Remove refnames from the cache */ - for (i = 0; i < n; i++) - if (remove_entry(packed, refnames[i]) != -1) + for_each_string_list_item(ref_to_delete, without) + if (remove_entry(packed, ref_to_delete->string) != -1) removed = 1; if (!removed) { /* @@ -3738,10 +3741,11 @@ static int ref_update_reject_duplicates(struct ref_update **updates, int n, int ref_transaction_commit(struct ref_transaction *transaction, struct strbuf *err) { - int ret = 0, delnum = 0, i; - const char **delnames; + int ret = 0, i; int n = transaction->nr; struct ref_update **updates = transaction->updates; + struct string_list refs_to_delete = STRING_LIST_INIT_NODUP; + struct string_list_item *ref_to_delete; assert(err); @@ -3753,9 +3757,6 @@ int ref_transaction_commit(struct ref_transaction *transaction, return 0; } - /* Allocate work space */ - delnames = xmalloc(sizeof(*delnames) * n); - /* Copy, sort, and reject duplicate refs */ qsort(updates, n, sizeof(*updates), ref_update_compare); if (ref_update_reject_duplicates(updates, n, err)) { @@ -3815,16 +3816,17 @@ int ref_transaction_commit(struct ref_transaction *transaction, } if (!(update->flags & REF_ISPRUNING)) - delnames[delnum++] = update->lock->ref_name; + string_list_append(&refs_to_delete, + update->lock->ref_name); } } - if (repack_without_refs(delnames, delnum, err)) { + if (repack_without_refs(&refs_to_delete, err)) { ret = TRANSACTION_GENERIC_ERROR; goto cleanup; } - for (i = 0; i < delnum; i++) - unlink_or_warn(git_path("logs/%s", delnames[i])); + for_each_string_list_item(ref_to_delete, &refs_to_delete) + unlink_or_warn(git_path("logs/%s", ref_to_delete->string)); clear_loose_ref_cache(&ref_cache); cleanup: @@ -3833,7 +3835,7 @@ cleanup: for (i = 0; i < n; i++) if (updates[i]->lock) unlock_ref(updates[i]->lock); - free(delnames); + string_list_clear(&refs_to_delete, 0); return ret; } diff --git a/refs.h b/refs.h index 2bc3556..5a0cd21 100644 --- a/refs.h +++ b/refs.h @@ -163,8 +163,15 @@ extern void rollback_packed_refs(void); */ int pack_refs(unsigned int flags); -extern int repack_without_refs(const char **refnames, int n, - struct strbuf *err); +/* + * Remove the refs listed in 'without' from the packed-refs file. + * On error, packed-refs will be unchanged, the return value is + * nonzero, and a message about the error is written to the 'err' + * strbuf. + * + * The refs in 'without' may have any order, the err buffer must not be ommited. + */ +extern int repack_without_refs(struct string_list *without, struct strbuf *err); extern int ref_exists(const char *); -- 2.2.0.rc2.13.g0786cdb ^ permalink raw reply related [flat|nested] 61+ messages in thread
* Re: [PATCH v4] refs.c: use a stringlist for repack_without_refs 2014-11-19 21:59 ` [PATCH v4] " Stefan Beller @ 2014-11-20 2:15 ` Jonathan Nieder 2014-11-20 16:47 ` Junio C Hamano 2014-11-20 18:04 ` [PATCH v5 1/1] " Stefan Beller 0 siblings, 2 replies; 61+ messages in thread From: Jonathan Nieder @ 2014-11-20 2:15 UTC (permalink / raw) To: Stefan Beller; +Cc: gitster, sahlberg, git Stefan Beller wrote: > From: Ronnie Sahlberg <sahlberg@google.com> > > This patch doesn't intend any functional changes. It is just > a refactoring, which replaces a char** array by a stringlist > in the function repack_without_refs. > This is easier to read and maintain as it delivers the same > functionality with less lines of code and less pointers. Thanks for the quick turnaround. Nit: please wrap to a consistent width and put a blank line between paragraphs. That is, the above should either say This patch doesn't intend any functional changes. It is just a refactoring to replace a char** array with a string_list in the function repack_without_refs. This is easier to read and maintain as it delivers the same functionality with less code and fewer pointers. or This patch doesn't intend any functional changes. It is just a refactoring to replace a char** array with a string_list in the function repack_without_refs. This is easier to read and maintain as it delivers the same functionality with less code and fewer pointers. Although I'm not sure the main benefit is having fewer asterisks. ;-) [...] > +++ b/builtin/remote.c [...] > @@ -1361,8 +1352,9 @@ static int prune_remote(const char *remote, int dry_run) > abbrev_ref(refname, "refs/remotes/")); > } > > - warn_dangling_symrefs(stdout, dangling_msg, &delete_refs_list); > - string_list_clear(&delete_refs_list, 0); > + sort_string_list(&delete_refs); > + warn_dangling_symrefs(stdout, dangling_msg, &delete_refs); > + string_list_clear(&delete_refs, 0); > > free_remote_ref_states(&states); > return result; Micronit: it would be clearer (and easier to remember to free the list in other code paths if this function gains more 'return' statements) with the string_list_clear in the same block as other code that frees resources (i.e., if the blank line moved one line up). [...] > --- a/refs.h > +++ b/refs.h > @@ -163,8 +163,15 @@ extern void rollback_packed_refs(void); > */ > int pack_refs(unsigned int flags); > > -extern int repack_without_refs(const char **refnames, int n, > - struct strbuf *err); > +/* > + * Remove the refs listed in 'without' from the packed-refs file. > + * On error, packed-refs will be unchanged, the return value is > + * nonzero, and a message about the error is written to the 'err' > + * strbuf. > + * > + * The refs in 'without' may have any order, the err buffer must not be ommited. Nits: s/ommited/omitted/ Comma splice. Long line. The function has to be able to write to 'err' on error, so I think the comment doesn't have to mention that err must be non-NULL. Any caller that tries to pass NULL will get an assertion error quickly. With or without the changes suggested above, Reviewed-by: Jonathan Nieder <jrnieder@gmail.com> ^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH v4] refs.c: use a stringlist for repack_without_refs 2014-11-20 2:15 ` Jonathan Nieder @ 2014-11-20 16:47 ` Junio C Hamano 2014-11-20 18:04 ` [PATCH v5 1/1] " Stefan Beller 1 sibling, 0 replies; 61+ messages in thread From: Junio C Hamano @ 2014-11-20 16:47 UTC (permalink / raw) To: Jonathan Nieder; +Cc: Stefan Beller, sahlberg, git Jonathan Nieder <jrnieder@gmail.com> writes: > [...] >> +++ b/builtin/remote.c > [...] >> @@ -1361,8 +1352,9 @@ static int prune_remote(const char *remote, int dry_run) >> abbrev_ref(refname, "refs/remotes/")); >> } >> >> - warn_dangling_symrefs(stdout, dangling_msg, &delete_refs_list); >> - string_list_clear(&delete_refs_list, 0); >> + sort_string_list(&delete_refs); >> + warn_dangling_symrefs(stdout, dangling_msg, &delete_refs); >> + string_list_clear(&delete_refs, 0); >> >> free_remote_ref_states(&states); >> return result; > > Micronit: it would be clearer (and easier to remember to free the list > in other code paths if this function gains more 'return' statements) > with the string_list_clear in the same block as other code that frees > resources (i.e., if the blank line moved one line up). Thanks for a careful reading. This kind of attention to detail helps the longer term health of the codebase. > The function has to be able to write to 'err' on error, so I think the > comment doesn't have to mention that err must be non-NULL. Any caller > that tries to pass NULL will get an assertion error quickly. That invites a bit of question, though. An equally plausible alternative definition for set of API functions that take strbuf *err is to pass it only when you care about the explanation of the error (i.e. it is valid for "git cmd --quiet" to pass NULL there) [*1*] (do we already have such a function?). And the comment may help clarifying which is which. I however think we shouldn't have mixtures (formatting into "strbuf *err" may be costly when we know we are asked to fail silently, but an error path is not usually performance sensitive). [Footnote] *1* With yet another one, a function may call error() on its own when a NULL is passed to strbuf *err, but let's not go there. ^ permalink raw reply [flat|nested] 61+ messages in thread
* [PATCH v5 1/1] refs.c: use a stringlist for repack_without_refs 2014-11-20 2:15 ` Jonathan Nieder 2014-11-20 16:47 ` Junio C Hamano @ 2014-11-20 18:04 ` Stefan Beller 2014-11-20 18:10 ` [PATCH] refs.c: repack_without_refs may be called without error string buffer Stefan Beller ` (3 more replies) 1 sibling, 4 replies; 61+ messages in thread From: Stefan Beller @ 2014-11-20 18:04 UTC (permalink / raw) To: gitster, sahlberg, git, jrnieder; +Cc: Stefan Beller From: Ronnie Sahlberg <sahlberg@google.com> This patch doesn't intend any functional changes. It is just a refactoring, which replaces a char** array by a stringlist in the function repack_without_refs. This is easier to read and maintain as it delivers the same functionality with less lines of code and more lines of documentation. [sb: ported this patch from a larger patch series to the master branch, added documentary comments in refs.h] Change-Id: Id7eaa821331f2ab89df063e1e76c8485dbcc3aed Signed-off-by: Ronnie Sahlberg <sahlberg@google.com> Signed-off-by: Stefan Beller <sbeller@google.com> Reviewed-by: Jonathan Nieder <jrnieder@gmail.com> --- Changes to version 1: * removed the double blank line * rename delete_refs_list to delete_refs * add back comments dropped by accident * use STRING_LIST_INIT_NODUP instead of the _DUP version in ref_transaction_commit * add documentary comments on the repack_without_refs function * user string_list_append instead of string_list_insert as it follows the previous behavior more closely. * put back the early exit of the loop in repack_without_refs Changes to version 2: * fixed commit message (comments after the three dashes) * fixed the curiosity Junio pointed out as it was just wrong code. Now it actually builds a list of all states.stale.items[i].util items. Changes in version 3: * reword commit message * sort delete_refs before passing it to warn_dangling_symrefs * change the comments (get back the one jrn complained about) in repack_without_refs * use the suggestion of jonathan for documenting repack_without_refs in the header. Add a note about the arguments. Changes in version 4: * I lied, when saying I had all the nits from Jonathan. I messed up the documentation in the header. This includes the documentary comment in the header. Changes in version 5: * Break lines as suggested by Jonathan, slightly rewording the commit message * have an empty line at another place in builtin/remote.c remove_branches to tell cleanup parts apart from actual work. * fix typo, improve documentary comment in refs.c * add Jonathans reviewed by Junio, I'll address your proposed changes in a different patch. If err is passed in as NULL, we'll just skip all the error string formatting and return silent and fast. builtin/remote.c | 32 ++++++++++++-------------------- refs.c | 38 ++++++++++++++++++++------------------ refs.h | 12 ++++++++++-- 3 files changed, 42 insertions(+), 40 deletions(-) diff --git a/builtin/remote.c b/builtin/remote.c index 7f28f92..364350a 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -750,16 +750,11 @@ static int mv(int argc, const char **argv) static int remove_branches(struct string_list *branches) { struct strbuf err = STRBUF_INIT; - const char **branch_names; int i, result = 0; - branch_names = xmalloc(branches->nr * sizeof(*branch_names)); - for (i = 0; i < branches->nr; i++) - branch_names[i] = branches->items[i].string; - if (repack_without_refs(branch_names, branches->nr, &err)) + if (repack_without_refs(branches, &err)) result |= error("%s", err.buf); strbuf_release(&err); - free(branch_names); for (i = 0; i < branches->nr; i++) { struct string_list_item *item = branches->items + i; @@ -1316,8 +1311,8 @@ static int prune_remote(const char *remote, int dry_run) { int result = 0, i; struct ref_states states; - struct string_list delete_refs_list = STRING_LIST_INIT_NODUP; - const char **delete_refs; + struct string_list delete_refs = STRING_LIST_INIT_NODUP; + struct string_list_item *ref; const char *dangling_msg = dry_run ? _(" %s will become dangling!") : _(" %s has become dangling!"); @@ -1325,6 +1320,9 @@ static int prune_remote(const char *remote, int dry_run) memset(&states, 0, sizeof(states)); get_remote_ref_states(remote, &states, GET_REF_STATES); + for (i = 0; i < states.stale.nr; i++) + string_list_append(&delete_refs, states.stale.items[i].util); + if (states.stale.nr) { printf_ln(_("Pruning %s"), remote); printf_ln(_("URL: %s"), @@ -1332,23 +1330,16 @@ static int prune_remote(const char *remote, int dry_run) ? states.remote->url[0] : _("(no URL)")); - delete_refs = xmalloc(states.stale.nr * sizeof(*delete_refs)); - for (i = 0; i < states.stale.nr; i++) - delete_refs[i] = states.stale.items[i].util; if (!dry_run) { struct strbuf err = STRBUF_INIT; - if (repack_without_refs(delete_refs, states.stale.nr, - &err)) + if (repack_without_refs(&delete_refs, &err)) result |= error("%s", err.buf); strbuf_release(&err); } - free(delete_refs); } - for (i = 0; i < states.stale.nr; i++) { - const char *refname = states.stale.items[i].util; - - string_list_insert(&delete_refs_list, refname); + for_each_string_list_item(ref, &delete_refs) { + const char *refname = ref->string; if (!dry_run) result |= delete_ref(refname, NULL, 0); @@ -1361,9 +1352,10 @@ static int prune_remote(const char *remote, int dry_run) abbrev_ref(refname, "refs/remotes/")); } - warn_dangling_symrefs(stdout, dangling_msg, &delete_refs_list); - string_list_clear(&delete_refs_list, 0); + sort_string_list(&delete_refs); + warn_dangling_symrefs(stdout, dangling_msg, &delete_refs); + string_list_clear(&delete_refs, 0); free_remote_ref_states(&states); return result; } diff --git a/refs.c b/refs.c index 5ff457e..ebcd90f 100644 --- a/refs.c +++ b/refs.c @@ -2639,23 +2639,26 @@ static int curate_packed_ref_fn(struct ref_entry *entry, void *cb_data) return 0; } -int repack_without_refs(const char **refnames, int n, struct strbuf *err) +int repack_without_refs(struct string_list *without, struct strbuf *err) { struct ref_dir *packed; struct string_list refs_to_delete = STRING_LIST_INIT_DUP; struct string_list_item *ref_to_delete; - int i, ret, removed = 0; + int ret, needs_repacking = 0, removed = 0; assert(err); /* Look for a packed ref */ - for (i = 0; i < n; i++) - if (get_packed_ref(refnames[i])) + for_each_string_list_item(ref_to_delete, without) { + if (get_packed_ref(ref_to_delete->string)) { + needs_repacking = 1; break; + } + } /* Avoid locking if we have nothing to do */ - if (i == n) - return 0; /* no refname exists in packed refs */ + if (!needs_repacking) + return 0; if (lock_packed_refs(0)) { unable_to_lock_message(git_path("packed-refs"), errno, err); @@ -2664,8 +2667,8 @@ int repack_without_refs(const char **refnames, int n, struct strbuf *err) packed = get_packed_refs(&ref_cache); /* Remove refnames from the cache */ - for (i = 0; i < n; i++) - if (remove_entry(packed, refnames[i]) != -1) + for_each_string_list_item(ref_to_delete, without) + if (remove_entry(packed, ref_to_delete->string) != -1) removed = 1; if (!removed) { /* @@ -3738,10 +3741,11 @@ static int ref_update_reject_duplicates(struct ref_update **updates, int n, int ref_transaction_commit(struct ref_transaction *transaction, struct strbuf *err) { - int ret = 0, delnum = 0, i; - const char **delnames; + int ret = 0, i; int n = transaction->nr; struct ref_update **updates = transaction->updates; + struct string_list refs_to_delete = STRING_LIST_INIT_NODUP; + struct string_list_item *ref_to_delete; assert(err); @@ -3753,9 +3757,6 @@ int ref_transaction_commit(struct ref_transaction *transaction, return 0; } - /* Allocate work space */ - delnames = xmalloc(sizeof(*delnames) * n); - /* Copy, sort, and reject duplicate refs */ qsort(updates, n, sizeof(*updates), ref_update_compare); if (ref_update_reject_duplicates(updates, n, err)) { @@ -3815,16 +3816,17 @@ int ref_transaction_commit(struct ref_transaction *transaction, } if (!(update->flags & REF_ISPRUNING)) - delnames[delnum++] = update->lock->ref_name; + string_list_append(&refs_to_delete, + update->lock->ref_name); } } - if (repack_without_refs(delnames, delnum, err)) { + if (repack_without_refs(&refs_to_delete, err)) { ret = TRANSACTION_GENERIC_ERROR; goto cleanup; } - for (i = 0; i < delnum; i++) - unlink_or_warn(git_path("logs/%s", delnames[i])); + for_each_string_list_item(ref_to_delete, &refs_to_delete) + unlink_or_warn(git_path("logs/%s", ref_to_delete->string)); clear_loose_ref_cache(&ref_cache); cleanup: @@ -3833,7 +3835,7 @@ cleanup: for (i = 0; i < n; i++) if (updates[i]->lock) unlock_ref(updates[i]->lock); - free(delnames); + string_list_clear(&refs_to_delete, 0); return ret; } diff --git a/refs.h b/refs.h index 2bc3556..c7323ff 100644 --- a/refs.h +++ b/refs.h @@ -163,8 +163,16 @@ extern void rollback_packed_refs(void); */ int pack_refs(unsigned int flags); -extern int repack_without_refs(const char **refnames, int n, - struct strbuf *err); +/* + * Remove the refs listed in 'without' from the packed-refs file. + * On error, packed-refs will be unchanged, the return value is + * nonzero, and a message about the error is written to the 'err' + * strbuf. + * + * The refs in 'without' may have any order. + * The err buffer must not be omitted. + */ +extern int repack_without_refs(struct string_list *without, struct strbuf *err); extern int ref_exists(const char *); -- 2.2.0.rc2.23.gca0107e ^ permalink raw reply related [flat|nested] 61+ messages in thread
* [PATCH] refs.c: repack_without_refs may be called without error string buffer 2014-11-20 18:04 ` [PATCH v5 1/1] " Stefan Beller @ 2014-11-20 18:10 ` Stefan Beller 2014-11-20 18:15 ` Ronnie Sahlberg 2014-11-20 18:35 ` Jonathan Nieder 2014-11-20 18:29 ` [PATCH v5 1/1] refs.c: use a stringlist for repack_without_refs Jonathan Nieder ` (2 subsequent siblings) 3 siblings, 2 replies; 61+ messages in thread From: Stefan Beller @ 2014-11-20 18:10 UTC (permalink / raw) To: gitster, git, sahlberg, jrnieder; +Cc: Stefan Beller If we don't pass in the error string buffer, we skip over all parts dealing with preparing error messages. Signed-off-by: Stefan Beller <sbeller@google.com> --- This goes ontop of [PATCH v5] refs.c: use a stringlist for repack_without_refs if that makes sense. refs.c | 8 ++++---- refs.h | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/refs.c b/refs.c index ebcd90f..3c85ea6 100644 --- a/refs.c +++ b/refs.c @@ -2646,8 +2646,6 @@ int repack_without_refs(struct string_list *without, struct strbuf *err) struct string_list_item *ref_to_delete; int ret, needs_repacking = 0, removed = 0; - assert(err); - /* Look for a packed ref */ for_each_string_list_item(ref_to_delete, without) { if (get_packed_ref(ref_to_delete->string)) { @@ -2661,7 +2659,9 @@ int repack_without_refs(struct string_list *without, struct strbuf *err) return 0; if (lock_packed_refs(0)) { - unable_to_lock_message(git_path("packed-refs"), errno, err); + if (err) + unable_to_lock_message(git_path("packed-refs"), + errno, err); return -1; } packed = get_packed_refs(&ref_cache); @@ -2688,7 +2688,7 @@ int repack_without_refs(struct string_list *without, struct strbuf *err) /* Write what remains */ ret = commit_packed_refs(); - if (ret) + if (ret && err) strbuf_addf(err, "unable to overwrite old ref-pack file: %s", strerror(errno)); return ret; diff --git a/refs.h b/refs.h index c7323ff..b71fb79 100644 --- a/refs.h +++ b/refs.h @@ -170,7 +170,6 @@ int pack_refs(unsigned int flags); * strbuf. * * The refs in 'without' may have any order. - * The err buffer must not be omitted. */ extern int repack_without_refs(struct string_list *without, struct strbuf *err); -- 2.2.0.rc2.23.gca0107e ^ permalink raw reply related [flat|nested] 61+ messages in thread
* Re: [PATCH] refs.c: repack_without_refs may be called without error string buffer 2014-11-20 18:10 ` [PATCH] refs.c: repack_without_refs may be called without error string buffer Stefan Beller @ 2014-11-20 18:15 ` Ronnie Sahlberg 2014-11-20 18:35 ` Jonathan Nieder 1 sibling, 0 replies; 61+ messages in thread From: Ronnie Sahlberg @ 2014-11-20 18:15 UTC (permalink / raw) To: Stefan Beller; +Cc: Junio C Hamano, git@vger.kernel.org, Jonathan Nieder On Thu, Nov 20, 2014 at 10:10 AM, Stefan Beller <sbeller@google.com> wrote: > If we don't pass in the error string buffer, we skip over all > parts dealing with preparing error messages. > > Signed-off-by: Stefan Beller <sbeller@google.com> > --- > > This goes ontop of [PATCH v5] refs.c: use a stringlist for repack_without_refs > if that makes sense. > > refs.c | 8 ++++---- > refs.h | 1 - > 2 files changed, 4 insertions(+), 5 deletions(-) > > diff --git a/refs.c b/refs.c > index ebcd90f..3c85ea6 100644 > --- a/refs.c > +++ b/refs.c > @@ -2646,8 +2646,6 @@ int repack_without_refs(struct string_list *without, struct strbuf *err) > struct string_list_item *ref_to_delete; > int ret, needs_repacking = 0, removed = 0; > > - assert(err); > - > /* Look for a packed ref */ > for_each_string_list_item(ref_to_delete, without) { > if (get_packed_ref(ref_to_delete->string)) { > @@ -2661,7 +2659,9 @@ int repack_without_refs(struct string_list *without, struct strbuf *err) > return 0; > > if (lock_packed_refs(0)) { > - unable_to_lock_message(git_path("packed-refs"), errno, err); > + if (err) > + unable_to_lock_message(git_path("packed-refs"), > + errno, err); > return -1; > } > packed = get_packed_refs(&ref_cache); > @@ -2688,7 +2688,7 @@ int repack_without_refs(struct string_list *without, struct strbuf *err) > > /* Write what remains */ > ret = commit_packed_refs(); > - if (ret) > + if (ret && err) > strbuf_addf(err, "unable to overwrite old ref-pack file: %s", > strerror(errno)); > return ret; > diff --git a/refs.h b/refs.h > index c7323ff..b71fb79 100644 > --- a/refs.h > +++ b/refs.h > @@ -170,7 +170,6 @@ int pack_refs(unsigned int flags); > * strbuf. > * > * The refs in 'without' may have any order. > - * The err buffer must not be omitted. > */ > extern int repack_without_refs(struct string_list *without, struct strbuf *err); > > -- > 2.2.0.rc2.23.gca0107e > LGTM Reviewed-by: Ronnie Sahlberg <sahlberg@google.com> Nit: While it does not hurt to allow passing NULL, at some stage later this function will become private to refs.c and ONLY be called from within transaction_commit() which will always pass a non-NULL err argument. At that stage we will not strictly need to allow err==NULL since all callers are guaranteed to always pass err!=NULL. That said, having err being optional is probably a better API. Maybe err should be made optional for all other functions that take an err strbuf too so that the calling conventions become more consistent? ^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH] refs.c: repack_without_refs may be called without error string buffer 2014-11-20 18:10 ` [PATCH] refs.c: repack_without_refs may be called without error string buffer Stefan Beller 2014-11-20 18:15 ` Ronnie Sahlberg @ 2014-11-20 18:35 ` Jonathan Nieder 2014-11-20 18:36 ` Ronnie Sahlberg 1 sibling, 1 reply; 61+ messages in thread From: Jonathan Nieder @ 2014-11-20 18:35 UTC (permalink / raw) To: Stefan Beller; +Cc: gitster, git, sahlberg Stefan Beller wrote: > If we don't pass in the error string buffer, we skip over all > parts dealing with preparing error messages. Please no. We tried this with the ref transaction code. When someone wants to silence the message, it is cheap enough to do struct strbuf ignore = STRBUF_INIT; if (thing_that_can_fail_in_an_ignorable_way(..., &ignore)) { ... handle the failure ... } The extra lines of code make it obvious that the error message is being dropped, which is a very good thing. The extra work to format a message in the error case is not so bad and can be mitigated if the error is a common normal case by passing a flag to not consider it an error. Silently losing good diagnostic messages when err == NULL would have the opposite effect: when there isn't a spare strbuf to put errors in around, it would be tempting for people coding in a hurry to just pass NULL, and to readers it would look at first glance like "oh, an optional paramter was not passed and we are getting the good default behavior". This is not a theoretical concern --- it actually happened. My two cents, Jonathan ^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH] refs.c: repack_without_refs may be called without error string buffer 2014-11-20 18:35 ` Jonathan Nieder @ 2014-11-20 18:36 ` Ronnie Sahlberg 2014-11-20 18:56 ` Stefan Beller 0 siblings, 1 reply; 61+ messages in thread From: Ronnie Sahlberg @ 2014-11-20 18:36 UTC (permalink / raw) To: Jonathan Nieder; +Cc: Stefan Beller, Junio C Hamano, git@vger.kernel.org On Thu, Nov 20, 2014 at 10:35 AM, Jonathan Nieder <jrnieder@gmail.com> wrote: > Stefan Beller wrote: > >> If we don't pass in the error string buffer, we skip over all >> parts dealing with preparing error messages. > > Please no. > > We tried this with the ref transaction code. When someone wants > to silence the message, it is cheap enough to do > > struct strbuf ignore = STRBUF_INIT; > > if (thing_that_can_fail_in_an_ignorable_way(..., &ignore)) { > ... handle the failure ... > } > > The extra lines of code make it obvious that the error message is > being dropped, which is a very good thing. The extra work to format a > message in the error case is not so bad and can be mitigated if the > error is a common normal case by passing a flag to not consider it an > error. > > Silently losing good diagnostic messages when err == NULL would have > the opposite effect: when there isn't a spare strbuf to put errors in > around, it would be tempting for people coding in a hurry to just pass > NULL, and to readers it would look at first glance like "oh, an > optional paramter was not passed and we are getting the good default > behavior". > > This is not a theoretical concern --- it actually happened. > Fair enough. Un-LGTM my message above. > My two cents, > Jonathan ^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH] refs.c: repack_without_refs may be called without error string buffer 2014-11-20 18:36 ` Ronnie Sahlberg @ 2014-11-20 18:56 ` Stefan Beller 0 siblings, 0 replies; 61+ messages in thread From: Stefan Beller @ 2014-11-20 18:56 UTC (permalink / raw) To: Ronnie Sahlberg; +Cc: Jonathan Nieder, Junio C Hamano, git@vger.kernel.org ok, will drop the patch due to bad design. On Thu, Nov 20, 2014 at 10:36 AM, Ronnie Sahlberg <sahlberg@google.com> wrote: > On Thu, Nov 20, 2014 at 10:35 AM, Jonathan Nieder <jrnieder@gmail.com> wrote: >> Stefan Beller wrote: >> >>> If we don't pass in the error string buffer, we skip over all >>> parts dealing with preparing error messages. >> >> Please no. >> >> We tried this with the ref transaction code. When someone wants >> to silence the message, it is cheap enough to do >> >> struct strbuf ignore = STRBUF_INIT; >> >> if (thing_that_can_fail_in_an_ignorable_way(..., &ignore)) { >> ... handle the failure ... >> } >> >> The extra lines of code make it obvious that the error message is >> being dropped, which is a very good thing. The extra work to format a >> message in the error case is not so bad and can be mitigated if the >> error is a common normal case by passing a flag to not consider it an >> error. >> >> Silently losing good diagnostic messages when err == NULL would have >> the opposite effect: when there isn't a spare strbuf to put errors in >> around, it would be tempting for people coding in a hurry to just pass >> NULL, and to readers it would look at first glance like "oh, an >> optional paramter was not passed and we are getting the good default >> behavior". >> >> This is not a theoretical concern --- it actually happened. >> > > Fair enough. > Un-LGTM my message above. > > > >> My two cents, >> Jonathan ^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH v5 1/1] refs.c: use a stringlist for repack_without_refs 2014-11-20 18:04 ` [PATCH v5 1/1] " Stefan Beller 2014-11-20 18:10 ` [PATCH] refs.c: repack_without_refs may be called without error string buffer Stefan Beller @ 2014-11-20 18:29 ` Jonathan Nieder 2014-11-20 18:37 ` Jonathan Nieder 2014-11-20 19:01 ` Junio C Hamano 3 siblings, 0 replies; 61+ messages in thread From: Jonathan Nieder @ 2014-11-20 18:29 UTC (permalink / raw) To: Stefan Beller; +Cc: gitster, sahlberg, git Stefan Beller wrote: > Change-Id: Id7eaa821331f2ab89df063e1e76c8485dbcc3aed Change-id snuck in. [...] > --- a/refs.h > +++ b/refs.h > @@ -163,8 +163,16 @@ extern void rollback_packed_refs(void); > */ > int pack_refs(unsigned int flags); > > -extern int repack_without_refs(const char **refnames, int n, > - struct strbuf *err); > +/* > + * Remove the refs listed in 'without' from the packed-refs file. > + * On error, packed-refs will be unchanged, the return value is > + * nonzero, and a message about the error is written to the 'err' > + * strbuf. > + * > + * The refs in 'without' may have any order. Tiny nit: this makes me wonder what the order represents --- how do I pick which order for the refs in without to have? I think the idea is just that 'without' doesn't have to be sorted (it's a shame we don't have separate sorted string list and unsorted string list types or a string_list_sorted() helper to catch bad callers early to functions that care). One way to say that would be Remove the refs listed in the unsorted string list 'without' from the packed-refs file. On error, [...] > + * The err buffer must not be omitted. s/buffer/strbuf/, or s/The err buffer/'err'/ s/omitted/NULL/ With the Change-Id dropped, and with or without the above comment nits addressed, Reviewed-by: Jonathan Nieder <jrnieder@gmail.com> ^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH v5 1/1] refs.c: use a stringlist for repack_without_refs 2014-11-20 18:04 ` [PATCH v5 1/1] " Stefan Beller 2014-11-20 18:10 ` [PATCH] refs.c: repack_without_refs may be called without error string buffer Stefan Beller 2014-11-20 18:29 ` [PATCH v5 1/1] refs.c: use a stringlist for repack_without_refs Jonathan Nieder @ 2014-11-20 18:37 ` Jonathan Nieder 2014-11-20 19:01 ` Junio C Hamano 3 siblings, 0 replies; 61+ messages in thread From: Jonathan Nieder @ 2014-11-20 18:37 UTC (permalink / raw) To: Stefan Beller; +Cc: gitster, sahlberg, git On Thu, Nov 20, 2014 at 10:04:26AM -0800, Stefan Beller wrote: > [Subject: refs.c: use a stringlist for repack_without_refs] One more nitpick. :) s/stringlist/string_list/ Thanks, Jonathan ^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH v5 1/1] refs.c: use a stringlist for repack_without_refs 2014-11-20 18:04 ` [PATCH v5 1/1] " Stefan Beller ` (2 preceding siblings ...) 2014-11-20 18:37 ` Jonathan Nieder @ 2014-11-20 19:01 ` Junio C Hamano 2014-11-20 19:05 ` Stefan Beller 3 siblings, 1 reply; 61+ messages in thread From: Junio C Hamano @ 2014-11-20 19:01 UTC (permalink / raw) To: Stefan Beller; +Cc: sahlberg, git, jrnieder Stefan Beller <sbeller@google.com> writes: > Junio, I'll address your proposed changes in a different patch. > If err is passed in as NULL, we'll just skip all the error string > formatting and return silent and fast. Huh, I lost track, but I never meant to say "the functions should return silently with error code when err == NULL". I said that it is another plausible expectation, hence justifies the comment to clarify, but wished that there were no need to clarify in the first place. If everybody required err != NULL, there would be no need to clarify which functions require err != NULL. If everybody accepted err == NULL as a more efficient way to do "--quiet", that is another way to remove the need to clarify. Either way is fine and I did not "propose" anything ;-). I think this matches more-or-less what I've locally tweaked after following the discussion between you and Jonathan. Thanks. ^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH v5 1/1] refs.c: use a stringlist for repack_without_refs 2014-11-20 19:01 ` Junio C Hamano @ 2014-11-20 19:05 ` Stefan Beller 2014-11-20 20:07 ` [PATCH v6] refs.c: use a string_list " Stefan Beller 0 siblings, 1 reply; 61+ messages in thread From: Stefan Beller @ 2014-11-20 19:05 UTC (permalink / raw) To: Junio C Hamano; +Cc: Ronnie Sahlberg, git@vger.kernel.org, Jonathan Nieder On Thu, Nov 20, 2014 at 11:01 AM, Junio C Hamano <gitster@pobox.com> wrote: > > I think this matches more-or-less what I've locally tweaked after > following the discussion between you and Jonathan. Thanks. Do you want me to resend the patch with Jonathans nits fixed? Jonathan wrote: >> Change-Id: Id7eaa821331f2ab89df063e1e76c8485dbcc3aed > Change-id snuck in. I need to get the format patch hook running, I was talking about earlier this week. ^ permalink raw reply [flat|nested] 61+ messages in thread
* [PATCH v6] refs.c: use a string_list for repack_without_refs 2014-11-20 19:05 ` Stefan Beller @ 2014-11-20 20:07 ` Stefan Beller 2014-11-20 20:36 ` Jonathan Nieder 0 siblings, 1 reply; 61+ messages in thread From: Stefan Beller @ 2014-11-20 20:07 UTC (permalink / raw) To: git, gitster, jrnieder; +Cc: Ronnie Sahlberg, Stefan Beller From: Ronnie Sahlberg <sahlberg@google.com> This patch doesn't intend any functional changes. It is just a refactoring, which replaces a char** array by a stringlist in the function repack_without_refs. This is easier to read and maintain as it delivers the same functionality with less lines of code and more lines of documentation. [sb: ported this patch from a larger patch series to the master branch, added documentary comments in refs.h] Signed-off-by: Ronnie Sahlberg <sahlberg@google.com> Signed-off-by: Stefan Beller <sbeller@google.com> Reviewed-by: Jonathan Nieder <jrnieder@gmail.com> --- Changes to version 1: * removed the double blank line * rename delete_refs_list to delete_refs * add back comments dropped by accident * use STRING_LIST_INIT_NODUP instead of the _DUP version in ref_transaction_commit * add documentary comments on the repack_without_refs function * user string_list_append instead of string_list_insert as it follows the previous behavior more closely. * put back the early exit of the loop in repack_without_refs Changes to version 2: * fixed commit message (comments after the three dashes) * fixed the curiosity Junio pointed out as it was just wrong code. Now it actually builds a list of all states.stale.items[i].util items. Changes in version 3: * reword commit message * sort delete_refs before passing it to warn_dangling_symrefs * change the comments (get back the one jrn complained about) in repack_without_refs * use the suggestion of jonathan for documenting repack_without_refs in the header. Add a note about the arguments. Changes in version 4: * I lied, when saying I had all the nits from Jonathan. I messed up the documentation in the header. This includes the documentary comment in the header. Changes in version 5: * Break lines as suggested by Jonathan, slightly rewording the commit message * have an empty line at another place in builtin/remote.c remove_branches to tell cleanup parts apart from actual work. * fix typo, improve documentary comment in refs.h * add Jonathans reviewed by Changes in version 6: * remove change id snuck in v5 * s/stringlist/string_list/ in commit message title * reworded the documentary comment in refs.h once again builtin/remote.c | 32 ++++++++++++-------------------- refs.c | 38 ++++++++++++++++++++------------------ refs.h | 12 ++++++++++-- 3 files changed, 42 insertions(+), 40 deletions(-) diff --git a/builtin/remote.c b/builtin/remote.c index 7f28f92..364350a 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -750,16 +750,11 @@ static int mv(int argc, const char **argv) static int remove_branches(struct string_list *branches) { struct strbuf err = STRBUF_INIT; - const char **branch_names; int i, result = 0; - branch_names = xmalloc(branches->nr * sizeof(*branch_names)); - for (i = 0; i < branches->nr; i++) - branch_names[i] = branches->items[i].string; - if (repack_without_refs(branch_names, branches->nr, &err)) + if (repack_without_refs(branches, &err)) result |= error("%s", err.buf); strbuf_release(&err); - free(branch_names); for (i = 0; i < branches->nr; i++) { struct string_list_item *item = branches->items + i; @@ -1316,8 +1311,8 @@ static int prune_remote(const char *remote, int dry_run) { int result = 0, i; struct ref_states states; - struct string_list delete_refs_list = STRING_LIST_INIT_NODUP; - const char **delete_refs; + struct string_list delete_refs = STRING_LIST_INIT_NODUP; + struct string_list_item *ref; const char *dangling_msg = dry_run ? _(" %s will become dangling!") : _(" %s has become dangling!"); @@ -1325,6 +1320,9 @@ static int prune_remote(const char *remote, int dry_run) memset(&states, 0, sizeof(states)); get_remote_ref_states(remote, &states, GET_REF_STATES); + for (i = 0; i < states.stale.nr; i++) + string_list_append(&delete_refs, states.stale.items[i].util); + if (states.stale.nr) { printf_ln(_("Pruning %s"), remote); printf_ln(_("URL: %s"), @@ -1332,23 +1330,16 @@ static int prune_remote(const char *remote, int dry_run) ? states.remote->url[0] : _("(no URL)")); - delete_refs = xmalloc(states.stale.nr * sizeof(*delete_refs)); - for (i = 0; i < states.stale.nr; i++) - delete_refs[i] = states.stale.items[i].util; if (!dry_run) { struct strbuf err = STRBUF_INIT; - if (repack_without_refs(delete_refs, states.stale.nr, - &err)) + if (repack_without_refs(&delete_refs, &err)) result |= error("%s", err.buf); strbuf_release(&err); } - free(delete_refs); } - for (i = 0; i < states.stale.nr; i++) { - const char *refname = states.stale.items[i].util; - - string_list_insert(&delete_refs_list, refname); + for_each_string_list_item(ref, &delete_refs) { + const char *refname = ref->string; if (!dry_run) result |= delete_ref(refname, NULL, 0); @@ -1361,9 +1352,10 @@ static int prune_remote(const char *remote, int dry_run) abbrev_ref(refname, "refs/remotes/")); } - warn_dangling_symrefs(stdout, dangling_msg, &delete_refs_list); - string_list_clear(&delete_refs_list, 0); + sort_string_list(&delete_refs); + warn_dangling_symrefs(stdout, dangling_msg, &delete_refs); + string_list_clear(&delete_refs, 0); free_remote_ref_states(&states); return result; } diff --git a/refs.c b/refs.c index 5ff457e..ebcd90f 100644 --- a/refs.c +++ b/refs.c @@ -2639,23 +2639,26 @@ static int curate_packed_ref_fn(struct ref_entry *entry, void *cb_data) return 0; } -int repack_without_refs(const char **refnames, int n, struct strbuf *err) +int repack_without_refs(struct string_list *without, struct strbuf *err) { struct ref_dir *packed; struct string_list refs_to_delete = STRING_LIST_INIT_DUP; struct string_list_item *ref_to_delete; - int i, ret, removed = 0; + int ret, needs_repacking = 0, removed = 0; assert(err); /* Look for a packed ref */ - for (i = 0; i < n; i++) - if (get_packed_ref(refnames[i])) + for_each_string_list_item(ref_to_delete, without) { + if (get_packed_ref(ref_to_delete->string)) { + needs_repacking = 1; break; + } + } /* Avoid locking if we have nothing to do */ - if (i == n) - return 0; /* no refname exists in packed refs */ + if (!needs_repacking) + return 0; if (lock_packed_refs(0)) { unable_to_lock_message(git_path("packed-refs"), errno, err); @@ -2664,8 +2667,8 @@ int repack_without_refs(const char **refnames, int n, struct strbuf *err) packed = get_packed_refs(&ref_cache); /* Remove refnames from the cache */ - for (i = 0; i < n; i++) - if (remove_entry(packed, refnames[i]) != -1) + for_each_string_list_item(ref_to_delete, without) + if (remove_entry(packed, ref_to_delete->string) != -1) removed = 1; if (!removed) { /* @@ -3738,10 +3741,11 @@ static int ref_update_reject_duplicates(struct ref_update **updates, int n, int ref_transaction_commit(struct ref_transaction *transaction, struct strbuf *err) { - int ret = 0, delnum = 0, i; - const char **delnames; + int ret = 0, i; int n = transaction->nr; struct ref_update **updates = transaction->updates; + struct string_list refs_to_delete = STRING_LIST_INIT_NODUP; + struct string_list_item *ref_to_delete; assert(err); @@ -3753,9 +3757,6 @@ int ref_transaction_commit(struct ref_transaction *transaction, return 0; } - /* Allocate work space */ - delnames = xmalloc(sizeof(*delnames) * n); - /* Copy, sort, and reject duplicate refs */ qsort(updates, n, sizeof(*updates), ref_update_compare); if (ref_update_reject_duplicates(updates, n, err)) { @@ -3815,16 +3816,17 @@ int ref_transaction_commit(struct ref_transaction *transaction, } if (!(update->flags & REF_ISPRUNING)) - delnames[delnum++] = update->lock->ref_name; + string_list_append(&refs_to_delete, + update->lock->ref_name); } } - if (repack_without_refs(delnames, delnum, err)) { + if (repack_without_refs(&refs_to_delete, err)) { ret = TRANSACTION_GENERIC_ERROR; goto cleanup; } - for (i = 0; i < delnum; i++) - unlink_or_warn(git_path("logs/%s", delnames[i])); + for_each_string_list_item(ref_to_delete, &refs_to_delete) + unlink_or_warn(git_path("logs/%s", ref_to_delete->string)); clear_loose_ref_cache(&ref_cache); cleanup: @@ -3833,7 +3835,7 @@ cleanup: for (i = 0; i < n; i++) if (updates[i]->lock) unlock_ref(updates[i]->lock); - free(delnames); + string_list_clear(&refs_to_delete, 0); return ret; } diff --git a/refs.h b/refs.h index 2bc3556..0bc6a1a 100644 --- a/refs.h +++ b/refs.h @@ -163,8 +163,16 @@ extern void rollback_packed_refs(void); */ int pack_refs(unsigned int flags); -extern int repack_without_refs(const char **refnames, int n, - struct strbuf *err); +/* + * Remove the refs listed in the unsorted string list 'without' from + * the packed-refs file. On error, packed-refs will be unchanged, the + * return value is nonzero, and a message about the error is written + * to the 'err' strbuf. + * + * The refs in 'without' may be unsorted. + * 'err' must not be NULL. + */ +extern int repack_without_refs(struct string_list *without, struct strbuf *err); extern int ref_exists(const char *); -- 2.2.0.rc2.23.gca0107e ^ permalink raw reply related [flat|nested] 61+ messages in thread
* Re: [PATCH v6] refs.c: use a string_list for repack_without_refs 2014-11-20 20:07 ` [PATCH v6] refs.c: use a string_list " Stefan Beller @ 2014-11-20 20:36 ` Jonathan Nieder 0 siblings, 0 replies; 61+ messages in thread From: Jonathan Nieder @ 2014-11-20 20:36 UTC (permalink / raw) To: Stefan Beller; +Cc: git, gitster, Ronnie Sahlberg Stefan Beller wrote: > Signed-off-by: Ronnie Sahlberg <sahlberg@google.com> > Signed-off-by: Stefan Beller <sbeller@google.com> > Reviewed-by: Jonathan Nieder <jrnieder@gmail.com> > --- Yep, looks good now. Thanks for bearing with me. [...] > +++ b/refs.h > @@ -163,8 +163,16 @@ extern void rollback_packed_refs(void); [...] > +/* > + * Remove the refs listed in the unsorted string list 'without' from > + * the packed-refs file. On error, packed-refs will be unchanged, the > + * return value is nonzero, and a message about the error is written > + * to the 'err' strbuf. > + * > + * The refs in 'without' may be unsorted. > + * 'err' must not be NULL. I think we've gone back and forth enough on this text and it's not worth the transactional cost to tweak it further, so I'm not suggesting a change --- just explaining how I read it for future reference. "may be unsorted" is confusing to me. It sounds like the reader of this comment (someone calling repack_without_refs) has to be prepared for that possibility. But we are saying the opposite --- not "be prepared", but "don't worry about sorting 'without', since repack_without_refs can handle it". It's also redundant, since the paragraph above already says that 'without' is an unsorted string list. The way I see it, there are four types that for various reasons (lack of language-level support for subclassing, etc) are conflated into a single struct in the string-list API: * sorted string list that owns its items (i.e., created with DUP) * sorted string list that does not own its items (i.e., created with NODUP) * unsorted string list that owns its items * unsorted string list that does not own its items Different functions are valid to call on each type, as documented in the comments in string-list.h. repack_without_refs accepts all 4 types of string-list. That's what it means when the documentation says its argument is unsorted. Thanks, Jonathan ^ permalink raw reply [flat|nested] 61+ messages in thread
* [PATCH 0/6] repack_without_refs(): convert to string_list 2014-11-19 18:50 ` Stefan Beller 2014-11-19 20:44 ` Jonathan Nieder @ 2014-11-21 14:09 ` Michael Haggerty 2014-11-21 14:09 ` [PATCH 1/6] prune_remote(): exit early if there are no stale references Michael Haggerty ` (6 more replies) 1 sibling, 7 replies; 61+ messages in thread From: Michael Haggerty @ 2014-11-21 14:09 UTC (permalink / raw) To: Stefan Beller, Junio C Hamano Cc: Jonathan Nieder, Ronnie Sahlberg, git, Michael Haggerty This is basically an atomized version of Ronnie/Jonathan/Stefan's patch [1] "refs.c: use a stringlist for repack_without_refs". But I've actually rewritten most of it from scratch, using the original patch as a reference. I was reviewing the original patch and it looked mostly OK [2], but I found it hard to read because it did several steps at once. So I tried to make the same basic change, but one baby step at a time. This is the result. I'm a known fanatic about making the smallest possible changes in each commit. The goal is to make the patch series as readable as possible, because reviewers' time is in shorter supply than coders' time. * Tiny little patches are IMO usually much easier to read than big ones, because there is less to keep in mind at a time. * Often tiny changes (e.g., renaming variables or functions) are so blindingly obvious that one only has to skim them, or even trust that the author, with the help of the compiler, could hardly have made a mistake [3]. * Using baby steps keeps the author from introducing unnecessary changes ("code churn"), by forcing him/her to justify each change on its own merits. * Using baby steps makes it harder for substantive changes to get overlooked or to sneak in without discussion [4]. * If there is a problem, baby commits can be bisected, usually making it obvious why the bug arose. * If the mailing list doesn't like part of the series, it is usually easier to omit a patch from the next reroll than to extract one change out of a patch that contains multiple logical changes. * It is often possible to arrange the order of the patches to give the patch series a good "narrative". Some members of the community probably disagree with me. Using baby step patches means that there is more mailing list traffic and more commits that accumulate in the project's history. There is sometimes a bit of extra to-and-fro as code is mutated incrementally. Or maybe other people can just keep more complicated changes in their heads at one time than I can. Nevertheless, I submit this version of the patch series for your amusement. Feel free to ignore it. [1] http://mid.gmane.org/1416434399-2303-1-git-send-email-sbeller@google.com [2] Problems that I noticed: * The commit message refers to "stringlist" where it should be "string_list". * One of the loops in prune_remote() iterates using indexes, while another loop (over the same string_list) uses for_each_string_list_item(). * The change from using string_list_insert() to string_list_append() in the same function, followed by sort_string_list(), doesn't remove duplicates as the old version did. The commit message should justify that this is OK. [3] I love the quote from C. A. R. Hoare: There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. I think the same thing applies to patches. [4] Case in point: when I was writing the commit message for patch 3/6, I realized that string_list_insert() omits duplicates whereas string_list_append() obviously doesn't. This aspect of the change wasn't justified. Do we have to add a call to string_list_remove_duplicates()? It turns out that the list cannot contain duplicates, but it took some digging to verify this. Michael Haggerty (6): prune_remote(): exit early if there are no stale references prune_remote(): initialize both delete_refs lists in a single loop prune_remote(): sort delete_refs_list references en masse repack_without_refs(): make the refnames argument a string_list prune_remote(): rename local variable prune_remote(): iterate using for_each_string_list_item() builtin/remote.c | 59 ++++++++++++++++++++++++++------------------------------ refs.c | 38 +++++++++++++++++++----------------- refs.h | 11 ++++++++++- 3 files changed, 57 insertions(+), 51 deletions(-) -- 2.1.3 ^ permalink raw reply [flat|nested] 61+ messages in thread
* [PATCH 1/6] prune_remote(): exit early if there are no stale references 2014-11-21 14:09 ` [PATCH 0/6] repack_without_refs(): convert to string_list Michael Haggerty @ 2014-11-21 14:09 ` Michael Haggerty 2014-11-22 21:07 ` Jonathan Nieder 2014-11-21 14:09 ` [PATCH 2/6] prune_remote(): initialize both delete_refs lists in a single loop Michael Haggerty ` (5 subsequent siblings) 6 siblings, 1 reply; 61+ messages in thread From: Michael Haggerty @ 2014-11-21 14:09 UTC (permalink / raw) To: Stefan Beller, Junio C Hamano Cc: Jonathan Nieder, Ronnie Sahlberg, git, Michael Haggerty Aside from making the logic clearer, this avoids a call to warn_dangling_symrefs(), which always does a for_each_rawref() iteration. Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu> --- builtin/remote.c | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/builtin/remote.c b/builtin/remote.c index 7f28f92..d2b684c 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -1325,25 +1325,28 @@ static int prune_remote(const char *remote, int dry_run) memset(&states, 0, sizeof(states)); get_remote_ref_states(remote, &states, GET_REF_STATES); - if (states.stale.nr) { - printf_ln(_("Pruning %s"), remote); - printf_ln(_("URL: %s"), - states.remote->url_nr - ? states.remote->url[0] - : _("(no URL)")); - - delete_refs = xmalloc(states.stale.nr * sizeof(*delete_refs)); - for (i = 0; i < states.stale.nr; i++) - delete_refs[i] = states.stale.items[i].util; - if (!dry_run) { - struct strbuf err = STRBUF_INIT; - if (repack_without_refs(delete_refs, states.stale.nr, - &err)) - result |= error("%s", err.buf); - strbuf_release(&err); - } - free(delete_refs); + if (!states.stale.nr) { + free_remote_ref_states(&states); + return 0; + } + + printf_ln(_("Pruning %s"), remote); + printf_ln(_("URL: %s"), + states.remote->url_nr + ? states.remote->url[0] + : _("(no URL)")); + + delete_refs = xmalloc(states.stale.nr * sizeof(*delete_refs)); + for (i = 0; i < states.stale.nr; i++) + delete_refs[i] = states.stale.items[i].util; + if (!dry_run) { + struct strbuf err = STRBUF_INIT; + if (repack_without_refs(delete_refs, states.stale.nr, + &err)) + result |= error("%s", err.buf); + strbuf_release(&err); } + free(delete_refs); for (i = 0; i < states.stale.nr; i++) { const char *refname = states.stale.items[i].util; -- 2.1.3 ^ permalink raw reply related [flat|nested] 61+ messages in thread
* Re: [PATCH 1/6] prune_remote(): exit early if there are no stale references 2014-11-21 14:09 ` [PATCH 1/6] prune_remote(): exit early if there are no stale references Michael Haggerty @ 2014-11-22 21:07 ` Jonathan Nieder 0 siblings, 0 replies; 61+ messages in thread From: Jonathan Nieder @ 2014-11-22 21:07 UTC (permalink / raw) To: Michael Haggerty; +Cc: Stefan Beller, Junio C Hamano, Ronnie Sahlberg, git Michael Haggerty wrote: > Aside from making the logic clearer, this avoids a call to > warn_dangling_symrefs(), which always does a for_each_rawref() > iteration. > > Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu> > --- > builtin/remote.c | 39 +++++++++++++++++++++------------------ > 1 file changed, 21 insertions(+), 18 deletions(-) I had been wondering about this but didn't chase it down far enough. Thanks for noticing and cleaning it up. Reviewed-by: Jonathan Nieder <jrnieder@gmail.com> ^ permalink raw reply [flat|nested] 61+ messages in thread
* [PATCH 2/6] prune_remote(): initialize both delete_refs lists in a single loop 2014-11-21 14:09 ` [PATCH 0/6] repack_without_refs(): convert to string_list Michael Haggerty 2014-11-21 14:09 ` [PATCH 1/6] prune_remote(): exit early if there are no stale references Michael Haggerty @ 2014-11-21 14:09 ` Michael Haggerty 2014-11-21 14:09 ` [PATCH 3/6] prune_remote(): sort delete_refs_list references en masse Michael Haggerty ` (4 subsequent siblings) 6 siblings, 0 replies; 61+ messages in thread From: Michael Haggerty @ 2014-11-21 14:09 UTC (permalink / raw) To: Stefan Beller, Junio C Hamano Cc: Jonathan Nieder, Ronnie Sahlberg, git, Michael Haggerty Also free them together at the end of the function. In a moment, the array version will become redundant. Managing them together makes later steps more obvious. Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu> --- builtin/remote.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/builtin/remote.c b/builtin/remote.c index d2b684c..d5a5a16 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -1337,8 +1337,13 @@ static int prune_remote(const char *remote, int dry_run) : _("(no URL)")); delete_refs = xmalloc(states.stale.nr * sizeof(*delete_refs)); - for (i = 0; i < states.stale.nr; i++) - delete_refs[i] = states.stale.items[i].util; + for (i = 0; i < states.stale.nr; i++) { + const char *refname = states.stale.items[i].util; + + delete_refs[i] = refname; + string_list_insert(&delete_refs_list, refname); + } + if (!dry_run) { struct strbuf err = STRBUF_INIT; if (repack_without_refs(delete_refs, states.stale.nr, @@ -1346,13 +1351,10 @@ static int prune_remote(const char *remote, int dry_run) result |= error("%s", err.buf); strbuf_release(&err); } - free(delete_refs); for (i = 0; i < states.stale.nr; i++) { const char *refname = states.stale.items[i].util; - string_list_insert(&delete_refs_list, refname); - if (!dry_run) result |= delete_ref(refname, NULL, 0); @@ -1365,8 +1367,9 @@ static int prune_remote(const char *remote, int dry_run) } warn_dangling_symrefs(stdout, dangling_msg, &delete_refs_list); - string_list_clear(&delete_refs_list, 0); + free(delete_refs); + string_list_clear(&delete_refs_list, 0); free_remote_ref_states(&states); return result; } -- 2.1.3 ^ permalink raw reply related [flat|nested] 61+ messages in thread
* [PATCH 3/6] prune_remote(): sort delete_refs_list references en masse 2014-11-21 14:09 ` [PATCH 0/6] repack_without_refs(): convert to string_list Michael Haggerty 2014-11-21 14:09 ` [PATCH 1/6] prune_remote(): exit early if there are no stale references Michael Haggerty 2014-11-21 14:09 ` [PATCH 2/6] prune_remote(): initialize both delete_refs lists in a single loop Michael Haggerty @ 2014-11-21 14:09 ` Michael Haggerty 2014-11-21 16:44 ` Junio C Hamano 2014-11-22 21:08 ` Jonathan Nieder 2014-11-21 14:09 ` [PATCH 4/6] repack_without_refs(): make the refnames argument a string_list Michael Haggerty ` (3 subsequent siblings) 6 siblings, 2 replies; 61+ messages in thread From: Michael Haggerty @ 2014-11-21 14:09 UTC (permalink / raw) To: Stefan Beller, Junio C Hamano Cc: Jonathan Nieder, Ronnie Sahlberg, git, Michael Haggerty Inserting items into a list in sorted order is O(N^2) whereas appending them unsorted and then sorting the list all at once is O(N lg N). string_list_insert() also removes duplicates, and this change loses that functionality. But the strings in this list, which ultimately come from a for_each_ref() iteration, cannot contain duplicates. Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu> --- builtin/remote.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/builtin/remote.c b/builtin/remote.c index d5a5a16..7d5c8d2 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -1341,8 +1341,9 @@ static int prune_remote(const char *remote, int dry_run) const char *refname = states.stale.items[i].util; delete_refs[i] = refname; - string_list_insert(&delete_refs_list, refname); + string_list_append(&delete_refs_list, refname); } + sort_string_list(&delete_refs_list); if (!dry_run) { struct strbuf err = STRBUF_INIT; -- 2.1.3 ^ permalink raw reply related [flat|nested] 61+ messages in thread
* Re: [PATCH 3/6] prune_remote(): sort delete_refs_list references en masse 2014-11-21 14:09 ` [PATCH 3/6] prune_remote(): sort delete_refs_list references en masse Michael Haggerty @ 2014-11-21 16:44 ` Junio C Hamano 2014-11-25 7:21 ` Michael Haggerty 2014-11-22 21:08 ` Jonathan Nieder 1 sibling, 1 reply; 61+ messages in thread From: Junio C Hamano @ 2014-11-21 16:44 UTC (permalink / raw) To: Michael Haggerty Cc: Stefan Beller, Jonathan Nieder, Ronnie Sahlberg, Git Mailing List On Fri, Nov 21, 2014 at 6:09 AM, Michael Haggerty <mhagger@alum.mit.edu> wrote: > Inserting items into a list in sorted order is O(N^2) whereas > appending them unsorted and then sorting the list all at once is > O(N lg N). > > string_list_insert() also removes duplicates, and this change loses > that functionality. But the strings in this list, which ultimately > come from a for_each_ref() iteration, cannot contain duplicates. > A similar conversion in other places we may do in the future might find a need for an equivalent to "-u" option of "sort" in the string_list_sort() function, but the above nicely explains why it is not necessary for this one. Good. Eh, why is that called sort_string_list()? Perhaps it is a good opening to introduce string_list_sort(list, flag) where flag would be a bitmask that represents ignore-case, uniquify, etc., and then either deprecate the current one or make it a thin wrapper of the one that is more consistently named. > Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu> > --- > builtin/remote.c | 3 ++- > 1 file changed, 2 insertions(+), 1 deletion(-) > > diff --git a/builtin/remote.c b/builtin/remote.c > index d5a5a16..7d5c8d2 100644 > --- a/builtin/remote.c > +++ b/builtin/remote.c > @@ -1341,8 +1341,9 @@ static int prune_remote(const char *remote, int dry_run) > const char *refname = states.stale.items[i].util; > > delete_refs[i] = refname; > - string_list_insert(&delete_refs_list, refname); > + string_list_append(&delete_refs_list, refname); > } > + sort_string_list(&delete_refs_list); > > if (!dry_run) { > struct strbuf err = STRBUF_INIT; > -- > 2.1.3 > ^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH 3/6] prune_remote(): sort delete_refs_list references en masse 2014-11-21 16:44 ` Junio C Hamano @ 2014-11-25 7:21 ` Michael Haggerty 2014-11-25 8:04 ` Michael Haggerty 0 siblings, 1 reply; 61+ messages in thread From: Michael Haggerty @ 2014-11-25 7:21 UTC (permalink / raw) To: Junio C Hamano Cc: Stefan Beller, Jonathan Nieder, Ronnie Sahlberg, Git Mailing List On 11/21/2014 05:44 PM, Junio C Hamano wrote: > On Fri, Nov 21, 2014 at 6:09 AM, Michael Haggerty <mhagger@alum.mit.edu> wrote: >> Inserting items into a list in sorted order is O(N^2) whereas >> appending them unsorted and then sorting the list all at once is >> O(N lg N). >> >> string_list_insert() also removes duplicates, and this change loses >> that functionality. But the strings in this list, which ultimately >> come from a for_each_ref() iteration, cannot contain duplicates. >> > > A similar conversion in other places we may do in the future > might find a need for an equivalent to "-u" option of "sort" in the > string_list_sort() function, but the above nicely explains why > it is not necessary for this one. Good. The only reason to integrate "-u" functionality into the sort would be if one expects a significant fraction of entries to be duplicates, in which case the sort could be structured to discard duplicates as it works, thereby reducing the work needed for the sort. I can't think of such a case in our code. Otherwise, calling sort_string_list() followed by string_list_remove_duplicates() should be just as clear and approximately as efficient. A couple of times I've also felt that an all-purpose *stable* sort would be convenient (though I can't remember the context offhand). I don't think we have such a thing. > Eh, why is that called sort_string_list()? Perhaps it is a good > opening to introduce string_list_sort(list, flag) where flag would > be a bitmask that represents ignore-case, uniquify, etc., and > then either deprecate the current one or make it a thin wrapper > of the one that is more consistently named. I agree. Indeed, I typed that function's name wrong once when constructing this patch. It would be better to name it consistently with the other string_list_*() functions. I put it on my todo list (but don't let that dissuade somebody else from doing it). Michael -- Michael Haggerty mhagger@alum.mit.edu ^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH 3/6] prune_remote(): sort delete_refs_list references en masse 2014-11-25 7:21 ` Michael Haggerty @ 2014-11-25 8:04 ` Michael Haggerty 0 siblings, 0 replies; 61+ messages in thread From: Michael Haggerty @ 2014-11-25 8:04 UTC (permalink / raw) To: Junio C Hamano Cc: Stefan Beller, Jonathan Nieder, Ronnie Sahlberg, Git Mailing List On 11/25/2014 08:21 AM, Michael Haggerty wrote: > On 11/21/2014 05:44 PM, Junio C Hamano wrote: >> [...] >> Eh, why is that called sort_string_list()? Perhaps it is a good >> opening to introduce string_list_sort(list, flag) where flag would >> be a bitmask that represents ignore-case, uniquify, etc., and >> then either deprecate the current one or make it a thin wrapper >> of the one that is more consistently named. > > I agree. Indeed, I typed that function's name wrong once when > constructing this patch. It would be better to name it consistently with > the other string_list_*() functions. > > I put it on my todo list (but don't let that dissuade somebody else from > doing it). Since I was re-rolling the patch series anyway, I tacked this renaming change onto the end of it. Feel free to omit it if you think it belongs separately. Michael -- Michael Haggerty mhagger@alum.mit.edu ^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH 3/6] prune_remote(): sort delete_refs_list references en masse 2014-11-21 14:09 ` [PATCH 3/6] prune_remote(): sort delete_refs_list references en masse Michael Haggerty 2014-11-21 16:44 ` Junio C Hamano @ 2014-11-22 21:08 ` Jonathan Nieder 1 sibling, 0 replies; 61+ messages in thread From: Jonathan Nieder @ 2014-11-22 21:08 UTC (permalink / raw) To: Michael Haggerty; +Cc: Stefan Beller, Junio C Hamano, Ronnie Sahlberg, git Michael Haggerty wrote: > Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu> > --- > builtin/remote.c | 3 ++- > 1 file changed, 2 insertions(+), 1 deletion(-) This and 2/6 are also Reviewed-by: Jonathan Nieder <jrnieder@gmail.com> ^ permalink raw reply [flat|nested] 61+ messages in thread
* [PATCH 4/6] repack_without_refs(): make the refnames argument a string_list 2014-11-21 14:09 ` [PATCH 0/6] repack_without_refs(): convert to string_list Michael Haggerty ` (2 preceding siblings ...) 2014-11-21 14:09 ` [PATCH 3/6] prune_remote(): sort delete_refs_list references en masse Michael Haggerty @ 2014-11-21 14:09 ` Michael Haggerty 2014-11-22 21:17 ` Jonathan Nieder 2014-11-21 14:09 ` [PATCH 5/6] prune_remote(): rename local variable Michael Haggerty ` (2 subsequent siblings) 6 siblings, 1 reply; 61+ messages in thread From: Michael Haggerty @ 2014-11-21 14:09 UTC (permalink / raw) To: Stefan Beller, Junio C Hamano Cc: Jonathan Nieder, Ronnie Sahlberg, git, Michael Haggerty All of the callers have string_lists available already, whereas two of them had to read data out of a string_list into an array of strings just to call this function. So change repack_without_refs() to take the list of refnames to omit as a string_list, and change the callers accordingly. Suggested-by: Ronnie Sahlberg <sahlberg@google.com> Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu> --- builtin/remote.c | 14 ++------------ refs.c | 38 ++++++++++++++++++++------------------ refs.h | 11 ++++++++++- 3 files changed, 32 insertions(+), 31 deletions(-) diff --git a/builtin/remote.c b/builtin/remote.c index 7d5c8d2..63a6709 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -750,16 +750,11 @@ static int mv(int argc, const char **argv) static int remove_branches(struct string_list *branches) { struct strbuf err = STRBUF_INIT; - const char **branch_names; int i, result = 0; - branch_names = xmalloc(branches->nr * sizeof(*branch_names)); - for (i = 0; i < branches->nr; i++) - branch_names[i] = branches->items[i].string; - if (repack_without_refs(branch_names, branches->nr, &err)) + if (repack_without_refs(branches, &err)) result |= error("%s", err.buf); strbuf_release(&err); - free(branch_names); for (i = 0; i < branches->nr; i++) { struct string_list_item *item = branches->items + i; @@ -1317,7 +1312,6 @@ static int prune_remote(const char *remote, int dry_run) int result = 0, i; struct ref_states states; struct string_list delete_refs_list = STRING_LIST_INIT_NODUP; - const char **delete_refs; const char *dangling_msg = dry_run ? _(" %s will become dangling!") : _(" %s has become dangling!"); @@ -1336,19 +1330,16 @@ static int prune_remote(const char *remote, int dry_run) ? states.remote->url[0] : _("(no URL)")); - delete_refs = xmalloc(states.stale.nr * sizeof(*delete_refs)); for (i = 0; i < states.stale.nr; i++) { const char *refname = states.stale.items[i].util; - delete_refs[i] = refname; string_list_append(&delete_refs_list, refname); } sort_string_list(&delete_refs_list); if (!dry_run) { struct strbuf err = STRBUF_INIT; - if (repack_without_refs(delete_refs, states.stale.nr, - &err)) + if (repack_without_refs(&delete_refs_list, &err)) result |= error("%s", err.buf); strbuf_release(&err); } @@ -1369,7 +1360,6 @@ static int prune_remote(const char *remote, int dry_run) warn_dangling_symrefs(stdout, dangling_msg, &delete_refs_list); - free(delete_refs); string_list_clear(&delete_refs_list, 0); free_remote_ref_states(&states); return result; diff --git a/refs.c b/refs.c index 5ff457e..b675e01 100644 --- a/refs.c +++ b/refs.c @@ -2639,22 +2639,25 @@ static int curate_packed_ref_fn(struct ref_entry *entry, void *cb_data) return 0; } -int repack_without_refs(const char **refnames, int n, struct strbuf *err) +int repack_without_refs(struct string_list *refnames, struct strbuf *err) { struct ref_dir *packed; struct string_list refs_to_delete = STRING_LIST_INIT_DUP; - struct string_list_item *ref_to_delete; - int i, ret, removed = 0; + struct string_list_item *refname, *ref_to_delete; + int ret, needs_repacking = 0, removed = 0; assert(err); /* Look for a packed ref */ - for (i = 0; i < n; i++) - if (get_packed_ref(refnames[i])) + for_each_string_list_item(refname, refnames) { + if (get_packed_ref(refname->string)) { + needs_repacking = 1; break; + } + } /* Avoid locking if we have nothing to do */ - if (i == n) + if (!needs_repacking) return 0; /* no refname exists in packed refs */ if (lock_packed_refs(0)) { @@ -2664,8 +2667,8 @@ int repack_without_refs(const char **refnames, int n, struct strbuf *err) packed = get_packed_refs(&ref_cache); /* Remove refnames from the cache */ - for (i = 0; i < n; i++) - if (remove_entry(packed, refnames[i]) != -1) + for_each_string_list_item(refname, refnames) + if (remove_entry(packed, refname->string) != -1) removed = 1; if (!removed) { /* @@ -3738,10 +3741,11 @@ static int ref_update_reject_duplicates(struct ref_update **updates, int n, int ref_transaction_commit(struct ref_transaction *transaction, struct strbuf *err) { - int ret = 0, delnum = 0, i; - const char **delnames; + int ret = 0, i; int n = transaction->nr; struct ref_update **updates = transaction->updates; + struct string_list refs_to_delete = STRING_LIST_INIT_NODUP; + struct string_list_item *ref_to_delete; assert(err); @@ -3753,9 +3757,6 @@ int ref_transaction_commit(struct ref_transaction *transaction, return 0; } - /* Allocate work space */ - delnames = xmalloc(sizeof(*delnames) * n); - /* Copy, sort, and reject duplicate refs */ qsort(updates, n, sizeof(*updates), ref_update_compare); if (ref_update_reject_duplicates(updates, n, err)) { @@ -3815,16 +3816,17 @@ int ref_transaction_commit(struct ref_transaction *transaction, } if (!(update->flags & REF_ISPRUNING)) - delnames[delnum++] = update->lock->ref_name; + string_list_append(&refs_to_delete, + update->lock->ref_name); } } - if (repack_without_refs(delnames, delnum, err)) { + if (repack_without_refs(&refs_to_delete, err)) { ret = TRANSACTION_GENERIC_ERROR; goto cleanup; } - for (i = 0; i < delnum; i++) - unlink_or_warn(git_path("logs/%s", delnames[i])); + for_each_string_list_item(ref_to_delete, &refs_to_delete) + unlink_or_warn(git_path("logs/%s", ref_to_delete->string)); clear_loose_ref_cache(&ref_cache); cleanup: @@ -3833,7 +3835,7 @@ cleanup: for (i = 0; i < n; i++) if (updates[i]->lock) unlock_ref(updates[i]->lock); - free(delnames); + string_list_clear(&refs_to_delete, 0); return ret; } diff --git a/refs.h b/refs.h index 2bc3556..90a4a40 100644 --- a/refs.h +++ b/refs.h @@ -163,7 +163,16 @@ extern void rollback_packed_refs(void); */ int pack_refs(unsigned int flags); -extern int repack_without_refs(const char **refnames, int n, +/* + * Remove the refs listed in 'refnames' from the packed-refs file. + * On error, packed-refs will be unchanged, the return value is + * nonzero, and a message about the error is written to the 'err' + * strbuf. + * + * The refs in 'refnames' needn't be sorted. The err buffer must not be + * omitted. + */ +extern int repack_without_refs(struct string_list *refnames, struct strbuf *err); extern int ref_exists(const char *); -- 2.1.3 ^ permalink raw reply related [flat|nested] 61+ messages in thread
* Re: [PATCH 4/6] repack_without_refs(): make the refnames argument a string_list 2014-11-21 14:09 ` [PATCH 4/6] repack_without_refs(): make the refnames argument a string_list Michael Haggerty @ 2014-11-22 21:17 ` Jonathan Nieder 2014-11-25 7:42 ` Michael Haggerty 0 siblings, 1 reply; 61+ messages in thread From: Jonathan Nieder @ 2014-11-22 21:17 UTC (permalink / raw) To: Michael Haggerty; +Cc: Stefan Beller, Junio C Hamano, Ronnie Sahlberg, git Michael Haggerty wrote: > All of the callers have string_lists available already Technically ref_transaction_commit doesn't, but that doesn't matter. > Suggested-by: Ronnie Sahlberg <sahlberg@google.com> > Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu> > --- > builtin/remote.c | 14 ++------------ > refs.c | 38 ++++++++++++++++++++------------------ > refs.h | 11 ++++++++++- > 3 files changed, 32 insertions(+), 31 deletions(-) Reviewed-by: Jonathan Nieder <jrnieder@gmail.com> One (optional) nit at the bottom of this message. [...] > +++ b/refs.c > @@ -2639,22 +2639,25 @@ static int curate_packed_ref_fn(struct ref_entry *entry, void *cb_data) > return 0; > } > > -int repack_without_refs(const char **refnames, int n, struct strbuf *err) > +int repack_without_refs(struct string_list *refnames, struct strbuf *err) > { > struct ref_dir *packed; > struct string_list refs_to_delete = STRING_LIST_INIT_DUP; > - struct string_list_item *ref_to_delete; > - int i, ret, removed = 0; > + struct string_list_item *refname, *ref_to_delete; > + int ret, needs_repacking = 0, removed = 0; > > assert(err); > > /* Look for a packed ref */ > - for (i = 0; i < n; i++) > - if (get_packed_ref(refnames[i])) > + for_each_string_list_item(refname, refnames) { > + if (get_packed_ref(refname->string)) { > + needs_repacking = 1; > break; > + } > + } > > /* Avoid locking if we have nothing to do */ > - if (i == n) > + if (!needs_repacking) This makes me wish C supported something like Python's for/else construct. Oh well. :) [...] > +++ b/refs.h > @@ -163,7 +163,16 @@ extern void rollback_packed_refs(void); > */ > int pack_refs(unsigned int flags); > > -extern int repack_without_refs(const char **refnames, int n, > +/* > + * Remove the refs listed in 'refnames' from the packed-refs file. > + * On error, packed-refs will be unchanged, the return value is > + * nonzero, and a message about the error is written to the 'err' > + * strbuf. > + * > + * The refs in 'refnames' needn't be sorted. The err buffer must not be > + * omitted. (nit) s/buffer/strbuf/, or s/The err buffer/'err'/ s/omitted/NULL/ Thanks, Jonathan ^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH 4/6] repack_without_refs(): make the refnames argument a string_list 2014-11-22 21:17 ` Jonathan Nieder @ 2014-11-25 7:42 ` Michael Haggerty 0 siblings, 0 replies; 61+ messages in thread From: Michael Haggerty @ 2014-11-25 7:42 UTC (permalink / raw) To: Jonathan Nieder; +Cc: Stefan Beller, Junio C Hamano, Ronnie Sahlberg, git On 11/22/2014 10:17 PM, Jonathan Nieder wrote: > Michael Haggerty wrote: > >> All of the callers have string_lists available already > > Technically ref_transaction_commit doesn't, but that doesn't matter. You're right. I'll correct this. >> Suggested-by: Ronnie Sahlberg <sahlberg@google.com> >> Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu> >> --- >> builtin/remote.c | 14 ++------------ >> refs.c | 38 ++++++++++++++++++++------------------ >> refs.h | 11 ++++++++++- >> 3 files changed, 32 insertions(+), 31 deletions(-) > > Reviewed-by: Jonathan Nieder <jrnieder@gmail.com> > > One (optional) nit at the bottom of this message. > > [...] >> +++ b/refs.c >> @@ -2639,22 +2639,25 @@ static int curate_packed_ref_fn(struct ref_entry *entry, void *cb_data) >> return 0; >> } >> >> -int repack_without_refs(const char **refnames, int n, struct strbuf *err) >> +int repack_without_refs(struct string_list *refnames, struct strbuf *err) >> { >> struct ref_dir *packed; >> struct string_list refs_to_delete = STRING_LIST_INIT_DUP; >> - struct string_list_item *ref_to_delete; >> - int i, ret, removed = 0; >> + struct string_list_item *refname, *ref_to_delete; >> + int ret, needs_repacking = 0, removed = 0; >> >> assert(err); >> >> /* Look for a packed ref */ >> - for (i = 0; i < n; i++) >> - if (get_packed_ref(refnames[i])) >> + for_each_string_list_item(refname, refnames) { >> + if (get_packed_ref(refname->string)) { >> + needs_repacking = 1; >> break; >> + } >> + } >> >> /* Avoid locking if we have nothing to do */ >> - if (i == n) >> + if (!needs_repacking) > > This makes me wish C supported something like Python's for/else > construct. Oh well. :) Ahhh, Python, where arrays of strings *are* string_lists :-) > [...] >> +++ b/refs.h >> @@ -163,7 +163,16 @@ extern void rollback_packed_refs(void); >> */ >> int pack_refs(unsigned int flags); >> >> -extern int repack_without_refs(const char **refnames, int n, >> +/* >> + * Remove the refs listed in 'refnames' from the packed-refs file. >> + * On error, packed-refs will be unchanged, the return value is >> + * nonzero, and a message about the error is written to the 'err' >> + * strbuf. >> + * >> + * The refs in 'refnames' needn't be sorted. The err buffer must not be >> + * omitted. > > (nit) > > s/buffer/strbuf/, or s/The err buffer/'err'/ > s/omitted/NULL/ I will fix this too (and improve the docstring a bit in general). Thanks for your careful review! Michael -- Michael Haggerty mhagger@alum.mit.edu ^ permalink raw reply [flat|nested] 61+ messages in thread
* [PATCH 5/6] prune_remote(): rename local variable 2014-11-21 14:09 ` [PATCH 0/6] repack_without_refs(): convert to string_list Michael Haggerty ` (3 preceding siblings ...) 2014-11-21 14:09 ` [PATCH 4/6] repack_without_refs(): make the refnames argument a string_list Michael Haggerty @ 2014-11-21 14:09 ` Michael Haggerty 2014-11-22 21:18 ` Jonathan Nieder 2014-11-21 14:09 ` [PATCH 6/6] prune_remote(): iterate using for_each_string_list_item() Michael Haggerty 2014-11-21 14:25 ` [PATCH 0/6] repack_without_refs(): convert to string_list Michael Haggerty 6 siblings, 1 reply; 61+ messages in thread From: Michael Haggerty @ 2014-11-21 14:09 UTC (permalink / raw) To: Stefan Beller, Junio C Hamano Cc: Jonathan Nieder, Ronnie Sahlberg, git, Michael Haggerty Rename "delete_refs_list" to "refs_to_prune". The new name is more self-explanatory. Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu> --- builtin/remote.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/builtin/remote.c b/builtin/remote.c index 63a6709..efbf5fb 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -1311,7 +1311,7 @@ static int prune_remote(const char *remote, int dry_run) { int result = 0, i; struct ref_states states; - struct string_list delete_refs_list = STRING_LIST_INIT_NODUP; + struct string_list refs_to_prune = STRING_LIST_INIT_NODUP; const char *dangling_msg = dry_run ? _(" %s will become dangling!") : _(" %s has become dangling!"); @@ -1333,13 +1333,13 @@ static int prune_remote(const char *remote, int dry_run) for (i = 0; i < states.stale.nr; i++) { const char *refname = states.stale.items[i].util; - string_list_append(&delete_refs_list, refname); + string_list_append(&refs_to_prune, refname); } - sort_string_list(&delete_refs_list); + sort_string_list(&refs_to_prune); if (!dry_run) { struct strbuf err = STRBUF_INIT; - if (repack_without_refs(&delete_refs_list, &err)) + if (repack_without_refs(&refs_to_prune, &err)) result |= error("%s", err.buf); strbuf_release(&err); } @@ -1358,9 +1358,9 @@ static int prune_remote(const char *remote, int dry_run) abbrev_ref(refname, "refs/remotes/")); } - warn_dangling_symrefs(stdout, dangling_msg, &delete_refs_list); + warn_dangling_symrefs(stdout, dangling_msg, &refs_to_prune); - string_list_clear(&delete_refs_list, 0); + string_list_clear(&refs_to_prune, 0); free_remote_ref_states(&states); return result; } -- 2.1.3 ^ permalink raw reply related [flat|nested] 61+ messages in thread
* Re: [PATCH 5/6] prune_remote(): rename local variable 2014-11-21 14:09 ` [PATCH 5/6] prune_remote(): rename local variable Michael Haggerty @ 2014-11-22 21:18 ` Jonathan Nieder 0 siblings, 0 replies; 61+ messages in thread From: Jonathan Nieder @ 2014-11-22 21:18 UTC (permalink / raw) To: Michael Haggerty; +Cc: Stefan Beller, Junio C Hamano, Ronnie Sahlberg, git Michael Haggerty wrote: > Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu> > --- > builtin/remote.c | 12 ++++++------ > 1 file changed, 6 insertions(+), 6 deletions(-) Reviewed-by: Jonathan Nieder <jrnieder@gmail.com> ^ permalink raw reply [flat|nested] 61+ messages in thread
* [PATCH 6/6] prune_remote(): iterate using for_each_string_list_item() 2014-11-21 14:09 ` [PATCH 0/6] repack_without_refs(): convert to string_list Michael Haggerty ` (4 preceding siblings ...) 2014-11-21 14:09 ` [PATCH 5/6] prune_remote(): rename local variable Michael Haggerty @ 2014-11-21 14:09 ` Michael Haggerty 2014-11-22 21:19 ` Jonathan Nieder 2014-11-21 14:25 ` [PATCH 0/6] repack_without_refs(): convert to string_list Michael Haggerty 6 siblings, 1 reply; 61+ messages in thread From: Michael Haggerty @ 2014-11-21 14:09 UTC (permalink / raw) To: Stefan Beller, Junio C Hamano Cc: Jonathan Nieder, Ronnie Sahlberg, git, Michael Haggerty Iterate over refs_to_prune using for_each_string_list_item() rather than writing out the loop in longhand. Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu> --- builtin/remote.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/builtin/remote.c b/builtin/remote.c index efbf5fb..7fec170 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -1309,9 +1309,10 @@ static int set_head(int argc, const char **argv) static int prune_remote(const char *remote, int dry_run) { - int result = 0, i; + int result = 0; struct ref_states states; struct string_list refs_to_prune = STRING_LIST_INIT_NODUP; + struct string_list_item *item; const char *dangling_msg = dry_run ? _(" %s will become dangling!") : _(" %s has become dangling!"); @@ -1330,11 +1331,8 @@ static int prune_remote(const char *remote, int dry_run) ? states.remote->url[0] : _("(no URL)")); - for (i = 0; i < states.stale.nr; i++) { - const char *refname = states.stale.items[i].util; - - string_list_append(&refs_to_prune, refname); - } + for_each_string_list_item(item, &states.stale) + string_list_append(&refs_to_prune, item->util); sort_string_list(&refs_to_prune); if (!dry_run) { @@ -1344,8 +1342,8 @@ static int prune_remote(const char *remote, int dry_run) strbuf_release(&err); } - for (i = 0; i < states.stale.nr; i++) { - const char *refname = states.stale.items[i].util; + for_each_string_list_item(item, &states.stale) { + const char *refname = item->util; if (!dry_run) result |= delete_ref(refname, NULL, 0); -- 2.1.3 ^ permalink raw reply related [flat|nested] 61+ messages in thread
* Re: [PATCH 6/6] prune_remote(): iterate using for_each_string_list_item() 2014-11-21 14:09 ` [PATCH 6/6] prune_remote(): iterate using for_each_string_list_item() Michael Haggerty @ 2014-11-22 21:19 ` Jonathan Nieder 0 siblings, 0 replies; 61+ messages in thread From: Jonathan Nieder @ 2014-11-22 21:19 UTC (permalink / raw) To: Michael Haggerty; +Cc: Stefan Beller, Junio C Hamano, Ronnie Sahlberg, git Michael Haggerty wrote: > Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu> > --- > builtin/remote.c | 14 ++++++-------- > 1 file changed, 6 insertions(+), 8 deletions(-) Reviewed-by: Jonathan Nieder <jrnieder@gmail.com> (That makes 6/6. :)) Thanks for your thoughtfulness in putting these together. They were pleasant to read. ^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH 0/6] repack_without_refs(): convert to string_list 2014-11-21 14:09 ` [PATCH 0/6] repack_without_refs(): convert to string_list Michael Haggerty ` (5 preceding siblings ...) 2014-11-21 14:09 ` [PATCH 6/6] prune_remote(): iterate using for_each_string_list_item() Michael Haggerty @ 2014-11-21 14:25 ` Michael Haggerty 2014-11-21 18:00 ` Junio C Hamano 6 siblings, 1 reply; 61+ messages in thread From: Michael Haggerty @ 2014-11-21 14:25 UTC (permalink / raw) To: Stefan Beller, Junio C Hamano; +Cc: Jonathan Nieder, Ronnie Sahlberg, git On 11/21/2014 03:09 PM, Michael Haggerty wrote: > This is basically an atomized version of Ronnie/Jonathan/Stefan's > patch [1] "refs.c: use a stringlist for repack_without_refs". But I've > actually rewritten most of it from scratch, using the original patch > as a reference. Naturally, right after I emailed this series I realized that there have been two more iterations on the original patch, which I overlooked because I was not CCed on them. (I'm not complaining, just explaining.) I don't think that those iterations changed anything substantial that overlaps with my version, but TBH it's such a pain in the ass working with patches in email that I don't think I'll go to the effort of checking for sure unless somebody shows interest in actually using my version. Sorry for being grumpy today :-( Michael -- Michael Haggerty mhagger@alum.mit.edu ^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH 0/6] repack_without_refs(): convert to string_list 2014-11-21 14:25 ` [PATCH 0/6] repack_without_refs(): convert to string_list Michael Haggerty @ 2014-11-21 18:00 ` Junio C Hamano 2014-11-21 19:57 ` Stefan Beller 2014-11-25 0:28 ` Our cumbersome mailing list workflow (was: Re: [PATCH 0/6] repack_without_refs(): convert to string_list) Michael Haggerty 0 siblings, 2 replies; 61+ messages in thread From: Junio C Hamano @ 2014-11-21 18:00 UTC (permalink / raw) To: Michael Haggerty; +Cc: Stefan Beller, Jonathan Nieder, Ronnie Sahlberg, git Michael Haggerty <mhagger@alum.mit.edu> writes: > I don't think that those iterations changed anything substantial that > overlaps with my version, but TBH it's such a pain in the ass working > with patches in email that I don't think I'll go to the effort of > checking for sure unless somebody shows interest in actually using my > version. > > Sorry for being grumpy today :-( Is the above meant as a grumpy rant to be ignored, or as a discussion starter to improve the colaboration to allow people to work better together instead of stepping on each other's patches? FWIW, I liked your rationale for "many smaller steps". One small uncomfort in that approach is that it often is not very obvious by reading "log -p master.." alone how well the end result fits together. Each individual step may make sense, or at least it may not make it any worse than the original, but until you apply the whole series and read "diff master..." in a sitting, it is somewhat hard to tell where you are going. But this is not "risk" or "bad thing"; just something that may make readers feel uncomfortable---we are not losing anything by splitting a series into small logical chunks. Thanks. ^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH 0/6] repack_without_refs(): convert to string_list 2014-11-21 18:00 ` Junio C Hamano @ 2014-11-21 19:57 ` Stefan Beller 2014-11-25 0:28 ` Our cumbersome mailing list workflow (was: Re: [PATCH 0/6] repack_without_refs(): convert to string_list) Michael Haggerty 1 sibling, 0 replies; 61+ messages in thread From: Stefan Beller @ 2014-11-21 19:57 UTC (permalink / raw) To: Junio C Hamano Cc: Michael Haggerty, Jonathan Nieder, Ronnie Sahlberg, git@vger.kernel.org On Fri, Nov 21, 2014 at 10:00 AM, Junio C Hamano <gitster@pobox.com> wrote: > Michael Haggerty <mhagger@alum.mit.edu> writes: > >> I don't think that those iterations changed anything substantial that >> overlaps with my version, but TBH it's such a pain in the ass working >> with patches in email that I don't think I'll go to the effort of >> checking for sure unless somebody shows interest in actually using my >> version. >> >> Sorry for being grumpy today :-( Sorry for causing the grumpyness. I have compared the versions, and they do look pretty similar. In refs.{c,h} we're just talking about variable names and comments, that are different. In remote.c prune_remote however we did have slight differences, * early exit vs a large body below an if * your approach seems more elegant to me as you seem to know what you're doing: for_each_string_list_item(item, &states.stale) string_list_append(&refs_to_prune, item->util); instead of for (i = 0; i < states.stale.nr; i++) string_list_append(&delete_refs, states.stale.items[i].util); * You do not have a sort_string_list at the end before warn_dangling_symrefs, but you explained that it is not necessary. On my continued journey on this mailing list I'll try to follow your example and write lots of small easy to review patches, as they are indeed way easier to follow. However as Junio mentioned, we get other problems having too small changes. In the review for the [PATCH v3 00/14] ref-transactions-reflog series you said: > I was reviewing this patch series (I left some comments in Gerrit about > the first few patches) when I realized that I'm having trouble > understanding the big picture of where you want to go with this. Maybe that was just my fault, not having stated the intentions in the cover letter explicit enough. But having many patches will also not help on presenting the big picture easily. Thanks for bearing with me, Stefan > > Is the above meant as a grumpy rant to be ignored, or as a > discussion starter to improve the colaboration to allow people to > work better together instead of stepping on each other's patches? > > FWIW, I liked your rationale for "many smaller steps". > > One small uncomfort in that approach is that it often is not very > obvious by reading "log -p master.." alone how well the end result > fits together. Each individual step may make sense, or at least it > may not make it any worse than the original, but until you apply the > whole series and read "diff master..." in a sitting, it is somewhat > hard to tell where you are going. But this is not "risk" or "bad > thing"; just something that may make readers feel uncomfortable---we > are not losing anything by splitting a series into small logical > chunks. > > Thanks. > > ^ permalink raw reply [flat|nested] 61+ messages in thread
* Our cumbersome mailing list workflow (was: Re: [PATCH 0/6] repack_without_refs(): convert to string_list) 2014-11-21 18:00 ` Junio C Hamano 2014-11-21 19:57 ` Stefan Beller @ 2014-11-25 0:28 ` Michael Haggerty 2014-11-27 17:46 ` Our cumbersome mailing list workflow Torsten Bögershausen 2014-12-03 23:57 ` Philip Oakley 1 sibling, 2 replies; 61+ messages in thread From: Michael Haggerty @ 2014-11-25 0:28 UTC (permalink / raw) To: Junio C Hamano; +Cc: Stefan Beller, Jonathan Nieder, Ronnie Sahlberg, git On 11/21/2014 07:00 PM, Junio C Hamano wrote: > Michael Haggerty <mhagger@alum.mit.edu> writes: > >> I don't think that those iterations changed anything substantial that >> overlaps with my version, but TBH it's such a pain in the ass working >> with patches in email that I don't think I'll go to the effort of >> checking for sure unless somebody shows interest in actually using my >> version. >> >> Sorry for being grumpy today :-( > > Is the above meant as a grumpy rant to be ignored, or as a > discussion starter to improve the colaboration to allow people to > work better together instead of stepping on each other's patches? I think I know the sentiments of the mailing list regulars well enough that it didn't seem worthwhile to open this topic again, so I was just letting off steam without any hope of changing anything. But since you asked... Let me list the aspects of our mailing list workflow that I find cumbersome as a contributor and reviewer: * Submitting patches to the mailing list is an ordeal of configuring format-patch and send-email and getting everything just right, using instructions that depend on the local environment. We saw that hardly any GSoC applicants were able to get it right on their first attempt. Submitting a patch series should be as simple as "git push". * Once patches are submitted, there is no assurance that you (Junio) will apply them to your tree at the same point that the submitter developed and tested them. * The branch name that you choose for a patch series is not easily derivable from the patches as they appeared in the mailing list. Trying to figure out whether/where the patches exist in your tree is a largely manual task. The reverse mapping, from in-tree commit to the email where it was proposed, is even more difficult to infer. * Your tree has no indication of which version of a patch series (v1, v2, etc) is currently applied. The previous three points combine to make it awkward to get patches into my local repository to review or test. There are two alternatives, both cumbersome and imprecise: * I do "git fetch gitster", then try to figure out whether the branch I'm interested in is present, what its name is, and whether the version in your tree is the latest version, then "git checkout xy/foobar". * Or I save the emails to a temporary directory (awkward because, Oh Horror, I use Thunderbird and not mutt as email client), hope that I've guessed the right place to apply them, run "git am", and later try to remember to clean up the temporary directory. * Once I've done that, the "supplemental" comments from the emails (the cover letter and the text under the "---") are nowhere available in the Git repository. So if I want to see the changes in context plus the supplemental comments, I have to jump back and forth between email client and Git repo. Plus I have to jump around the rest of the email thread to see what comments other reviewers have already made about the series. * Following patch series across iterations is also awkward. To compare two versions, I have to first get both patch series into my repo, which involves digging through the ML history to find older versions, followed by the "git am" steps. Often submitters are nice enough to put links to previous versions of their patch series in their cover letters, but the links are to a web-based email archive, from which it is even more awkward to grab and apply patches. So in practice I then go back to my email client and search my local archive for my copy of the same email that was referenced in the archive, and apply the patch from there. Finding comments about old versions of a patch series is nearly as much work. * Because of the indeterminate application point, accumulating Signed-off-by lines, changed committer metadata, and maintainer tweaks, the commits that make it to the official tree have different SHA-1s than the commits in the submitter's tree, and both are different than the commits in the tree of any reviewer who got the patches using "git am". This makes it hard to be sure that everybody is on the same page. It also makes it awkward for people to exchange ideas for further changes via Git protocols in the form of patches. * Because of the crude serialization of patches through email, it is only possible to submit linear patch series, not merge commits. Hmmm, I think that covers most of the problems of handling patches and review via a mailing list. What are some alternatives? I did enjoy the variety of reviewing some patch series using Gerrit. It is nice that it tracks the evolution of a patch from version to version, and that the comments made on all versions of a patch are summarized in a single place. This makes it easier to avoid commenting on issues that other reviewers have already noted and easier to check that your own comments have been addressed by later versions of the patch. On the other hand, Gerrit seems strongly focused on individual patches rather than on patch series (which might not match our workflow so well), the UI is overwhelming (though I think one could get quite productive with it if one used it every day), and the notification emails come in blizzards. GitHub is another obvious alternative [1], free for open-source projects albeit not open-source itself. It is very easy to use and easy to interact with from a Git client, and also has a good API. It is super easy to submit patches to a project using GitHub. But the GitHub user interface (ISTM) is optimized for getting the net result of an entire feature branch perfect, as opposed to iterating a series of patches until each one is individually perfect (e.g., it works best when adding patches on top of a feature branch as opposed to rebasing existing patches). Since Git development is oriented towards perfecting each patch, GitHub would be a bit of an impedance mismatch. I don't think either of those systems is ideally matched to the Git project's workflow, but in my opinion either one of them would be more convenient for contributors and reviewers than serializing everything through the mailing list. Of course what is most convenient for the maintainer is of huge importance, but I can't say much about that. > FWIW, I liked your rationale for "many smaller steps". Thanks. > One small uncomfort in that approach is that it often is not very > obvious by reading "log -p master.." alone how well the end result > fits together. Each individual step may make sense, or at least it > may not make it any worse than the original, but until you apply the > whole series and read "diff master..." in a sitting, it is somewhat > hard to tell where you are going. But this is not "risk" or "bad > thing"; just something that may make readers feel uncomfortable---we > are not losing anything by splitting a series into small logical > chunks. Ideally, the cover letter should provide the "big picture" rationale for a patch series, and the individual commit messages should provide clues about why that step is useful. It might be a nice convention to ask people to write the "big picture" rationale in their cover letter, separated by a "---" from non-permanent discussion. Then the part above the "---" could be copied into the commit message for the *merge commit* that brings the feature branch into master. That would preserve it for posterity in a place where it is relatively easy to find. But I am reluctant to make the process of submitting patches even more difficult :-) Michael [1] Disclaimer: I work for GitHub. -- Michael Haggerty mhagger@alum.mit.edu ^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: Our cumbersome mailing list workflow 2014-11-25 0:28 ` Our cumbersome mailing list workflow (was: Re: [PATCH 0/6] repack_without_refs(): convert to string_list) Michael Haggerty @ 2014-11-27 17:46 ` Torsten Bögershausen 2014-11-27 18:24 ` Matthieu Moy ` (2 more replies) 2014-12-03 23:57 ` Philip Oakley 1 sibling, 3 replies; 61+ messages in thread From: Torsten Bögershausen @ 2014-11-27 17:46 UTC (permalink / raw) To: Michael Haggerty, Junio C Hamano Cc: Stefan Beller, Jonathan Nieder, Ronnie Sahlberg, git On 2014-11-25 01.28, Michael Haggerty wrote: [] > Let me list the aspects of our mailing list workflow that I find > cumbersome as a contributor and reviewer: > > * Submitting patches to the mailing list is an ordeal of configuring > format-patch and send-email and getting everything just right, using > instructions that depend on the local environment. Typically everything fits into ~/.gitconfig, which can be carried around on a USB-Stick. Is there any details which I miss, or howtows we can improve ? > We saw that hardly > any GSoC applicants were able to get it right on their first attempt. > Submitting a patch series should be as simple as "git push". > > * Once patches are submitted, there is no assurance that you (Junio) > will apply them to your tree at the same point that the submitter > developed and tested them. > > * The branch name that you choose for a patch series is not easily > derivable from the patches as they appeared in the mailing list. Trying > to figure out whether/where the patches exist in your tree is a largely > manual task. The reverse mapping, from in-tree commit to the email where > it was proposed, is even more difficult to infer. > > * Your tree has no indication of which version of a patch series (v1, > v2, etc) is currently applied. > > The previous three points combine to make it awkward to get patches into > my local repository to review or test. There are two alternatives, both > cumbersome and imprecise: > > * I do "git fetch gitster", then try to figure out whether the branch > I'm interested in is present, what its name is, and whether the version > in your tree is the latest version, then "git checkout xy/foobar". There are 12 branches from mh/, so it should be possible to find the name, und run git log gitster/xy/fix_this_bug or so. Even more important, this branch is the "single point of truth", because this branch may be merged eventually, and nothing else. > > * Or I save the emails to a temporary directory (awkward because, Oh > Horror, I use Thunderbird and not mutt as email client), hope that I've > guessed the right place to apply them, run "git am", and later try to > remember to clean up the temporary directory. Is there a "mutt howto" somewhere? > > * Once I've done that, the "supplemental" comments from the emails (the > cover letter and the text under the "---") are nowhere available in the > Git repository. So if I want to see the changes in context plus the > supplemental comments, I have to jump back and forth between email > client and Git repo. Plus I have to jump around the rest of the email > thread to see what comments other reviewers have already made about the > series. > > * Following patch series across iterations is also awkward. To compare > two versions, I have to first get both patch series into my repo, which > involves digging through the ML history to find older versions, followed > by the "git am" steps. Often submitters are nice enough to put links to > previous versions of their patch series in their cover letters, but the > links are to a web-based email archive, from which it is even more > awkward to grab and apply patches. So in practice I then go back to my > email client and search my local archive for my copy of the same email > that was referenced in the archive, and apply the patch from there. > Finding comments about old versions of a patch series is nearly as much > work. In short: We can ask every contributor, if the patch send to the mailing list is available on a public Git-repo, and what the branch name is, like _V2.. Does this makes sense ? As an alternative, you can save the branches locally, after running git-am once, just keep the branch. [] > > I did enjoy the variety of reviewing some patch series using Gerrit. It > is nice that it tracks the evolution of a patch from version to version, > and that the comments made on all versions of a patch are summarized in > a single place. This makes it easier to avoid commenting on issues that > other reviewers have already noted and easier to check that your own > comments have been addressed by later versions of the patch. On the > other hand, Gerrit seems strongly focused on individual patches rather > than on patch series (which might not match our workflow so well), the > UI is overwhelming (though I think one could get quite productive with > it if one used it every day), and the notification emails come in blizzards. > > Michael > > [1] Disclaimer: I work for GitHub. > I like Gerrit as well. But it is less efficient to use, a WEB browser is slower (often), and you need to use the mouse... However, if you put your patches on Gerrit, and add the link in your cover-letter, it may be worth a trial. But there is another thing: Once a patch is send out, I would ask the sender to wait and collect comments at least 24 hours before sending a V2. We all living in different time zones, so please let the world spin once. My feeling is that a patch > 5 commits should have a waiting time > 5 days, otherwise I start reviewing V1, then V2 comes, then V3 before I am finished with V1. That is not ideal. What does it cost to push your branch to a public repo and include that information in the email ? And how feasable/nice/useful is it to ask contributers for a wait time between re-rolling ? ^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: Our cumbersome mailing list workflow 2014-11-27 17:46 ` Our cumbersome mailing list workflow Torsten Bögershausen @ 2014-11-27 18:24 ` Matthieu Moy 2014-11-28 12:09 ` Philip Oakley 2014-11-27 22:53 ` Eric Wong 2014-11-28 14:31 ` Michael Haggerty 2 siblings, 1 reply; 61+ messages in thread From: Matthieu Moy @ 2014-11-27 18:24 UTC (permalink / raw) To: Torsten Bögershausen Cc: Michael Haggerty, Junio C Hamano, Stefan Beller, Jonathan Nieder, Ronnie Sahlberg, git Torsten Bögershausen <tboegi@web.de> writes: > On 2014-11-25 01.28, Michael Haggerty wrote: > [] >> Let me list the aspects of our mailing list workflow that I find >> cumbersome as a contributor and reviewer: >> >> * Submitting patches to the mailing list is an ordeal of configuring >> format-patch and send-email and getting everything just right, using >> instructions that depend on the local environment. > Typically everything fits into ~/.gitconfig, > which can be carried around on a USB-Stick. I personnally submit all my Git patches from a machine whose /usr/sbin/sendmail knows how to send emails, so for me configuration is super simple. But I can imagine the pain of someone working on various machines with various network configuration and normally using a webmail to send emails. Sharing ~/.gitconfig does not always work because on machine A you only can use one SMTP server, and on machine B only another ... -- Matthieu Moy http://www-verimag.imag.fr/~moy/ ^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: Our cumbersome mailing list workflow 2014-11-27 18:24 ` Matthieu Moy @ 2014-11-28 12:09 ` Philip Oakley 0 siblings, 0 replies; 61+ messages in thread From: Philip Oakley @ 2014-11-28 12:09 UTC (permalink / raw) To: Matthieu Moy, Torsten Bögershausen Cc: Michael Haggerty, Junio C Hamano, Stefan Beller, Jonathan Nieder, Ronnie Sahlberg, git From: "Matthieu Moy" <Matthieu.Moy@grenoble-inp.fr> > Torsten Bögershausen <tboegi@web.de> writes: > >> On 2014-11-25 01.28, Michael Haggerty wrote: >> [] >>> Let me list the aspects of our mailing list workflow that I find >>> cumbersome as a contributor and reviewer: >>> >>> * Submitting patches to the mailing list is an ordeal of configuring >>> format-patch and send-email and getting everything just right, using >>> instructions that depend on the local environment. >> Typically everything fits into ~/.gitconfig, >> which can be carried around on a USB-Stick. > > I personnally submit all my Git patches from a machine whose > /usr/sbin/sendmail knows how to send emails, so for me configuration > is > super simple. But I can imagine the pain of someone working on various > machines with various network configuration and normally using a > webmail > to send emails. Sharing ~/.gitconfig does not always work because on > machine A you only can use one SMTP server, and on machine B only > another ... The bit I find awkward for the send-email step is the creation of the "to" and "cc" lists. I tend to create the command line in a separate file so that I can re-use it for V2 etc. and even then I end up with all patches going to the full to/cc list. Michael's original discussion email did feel to summarise the isses [1] well. -- Philip [1] System Problems are Wicked problems : http://en.wikipedia.org/wiki/Wicked_problem www.poppendieck.com/wicked.htm ^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: Our cumbersome mailing list workflow 2014-11-27 17:46 ` Our cumbersome mailing list workflow Torsten Bögershausen 2014-11-27 18:24 ` Matthieu Moy @ 2014-11-27 22:53 ` Eric Wong 2014-11-28 15:34 ` Michael Haggerty 2014-11-28 14:31 ` Michael Haggerty 2 siblings, 1 reply; 61+ messages in thread From: Eric Wong @ 2014-11-27 22:53 UTC (permalink / raw) To: Torsten Bögershausen Cc: Michael Haggerty, Junio C Hamano, Stefan Beller, Jonathan Nieder, Ronnie Sahlberg, git Torsten Bögershausen <tboegi@web.de> wrote: > On 2014-11-25 01.28, Michael Haggerty wrote: > > * Or I save the emails to a temporary directory (awkward because, Oh > > Horror, I use Thunderbird and not mutt as email client), hope that I've > > guessed the right place to apply them, run "git am", and later try to > > remember to clean up the temporary directory. > > Is there a "mutt howto" somewhere? Not that I'm aware of, but Documentation/email-clients.txt in the Linux kernel has some short notes... My muttrc has had the following since my early days as a git user: macro index A ":unset pipe_decode\n|git am -3\n:set pipe_decode\n" macro pager A ":unset pipe_decode\n|git am -3\n:set pipe_decode\n" (Hit Shift-A while viewing/selecting a message to apply a patch, it requires you run mutt in your project working directory, though). Perhaps there can be a similar document or reference to it in our Documentation/ > In short: > We can ask every contributor, if the patch send to the mailing list > is available on a public Git-repo, and what the branch name is, > like _V2.. Does this makes sense ? Not unreasonable. I hope that won't give folks an excuse to refuse to mail patches, though. Some folks read email offline and can't fetch repos until they're online again. > I like Gerrit as well. > But it is less efficient to use, a WEB browser is slower (often), and > you need to use the mouse... IMNSHO, development of non-graphical software should never depend on graphical software. Also, I guess there is no way to comment on Gerrit via email (without registration/logins?). Lately, I've been trying to think of ways to make collaboration less centralized. Moving to more centralized collaboration tools is a step back for decentralized VCS. > But there is another thing: > Once a patch is send out, I would ask the sender to wait and collect comments > at least 24 hours before sending a V2. > We all living in different time zones, so please let the world spin once. > > My feeling is that a patch > 5 commits should have > a waiting time > 5 days, otherwise I start reviewing V1, then V2 comes, > then V3 before I am finished with V1. That is not ideal. > > What does it cost to push your branch to a public repo and > include that information in the email ? > > And how feasable/nice/useful is it to ask contributers for a wait > time between re-rolling ? All that sounds good. ^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: Our cumbersome mailing list workflow 2014-11-27 22:53 ` Eric Wong @ 2014-11-28 15:34 ` Michael Haggerty 2014-11-28 16:24 ` brian m. carlson 2014-12-01 2:46 ` Junio C Hamano 0 siblings, 2 replies; 61+ messages in thread From: Michael Haggerty @ 2014-11-28 15:34 UTC (permalink / raw) To: Eric Wong, Torsten Bögershausen Cc: Junio C Hamano, Stefan Beller, Jonathan Nieder, Ronnie Sahlberg, git On 11/27/2014 11:53 PM, Eric Wong wrote: > Torsten Bögershausen <tboegi@web.de> wrote: >> On 2014-11-25 01.28, Michael Haggerty wrote: >>> [...] >> In short: >> We can ask every contributor, if the patch send to the mailing list >> is available on a public Git-repo, and what the branch name is, >> like _V2.. Does this makes sense ? > > Not unreasonable. I hope that won't give folks an excuse to refuse > to mail patches, though. Some folks read email offline and can't > fetch repos until they're online again. My ideal would be to invert the procedure. Let the patches in a public Git repository somewhere be the primary artifact, and let the review process be focused there. Let email be an alternative interface to the central review site: * Generate patch emails (similar to the current format) when pull requests are submitted. * Generate notification emails when people comment on the patches. * Allow people to respond to the patch and notification emails via email. The central review site should associate those comments with the patches that they apply to, and present them along with other review comments received via other interfaces. >> I like Gerrit as well. >> But it is less efficient to use, a WEB browser is slower (often), and >> you need to use the mouse... > > IMNSHO, development of non-graphical software should never depend on > graphical software. Also, I guess there is no way to comment on Gerrit > via email (without registration/logins?). The days of the vt52 are over. I'm an old neckbeard myself and have used *real* vt52s. But these days even my *cellphone* is able to handle the GitHub website [1]. Rejecting modern technology is not intrinsically virtuous; it only makes sense if the old technology is really superior. And it is not enough for it to be superior only for neckbeards; it should be superior when averaged over all of the people whose participation we would like to have in the Git project. And by the way, there are text-only clients for interacting with GitHub [1]. > Lately, I've been trying to think of ways to make collaboration less > centralized. Moving to more centralized collaboration tools is a step > back for decentralized VCS. If an efficient decentralized collaboration system existed, then I'd love to give it a chance. But as far as I know, the existing systems are all embryonic. Don't forget that even our current system is centralized to some extent. There is a single mailing list through which all emails pass. There are a few email archives that we de facto rely on (and it is a brittle dependency--if Gmane were to disappear, we would have an awful lot of broken URLs in our emails that would be impossible to fix). It seems like a few desirable features are being talked about here, and summarizing the discussion as "centralized" vs "decentralized" is too simplistic. What is really important? 1. Convenient and efficient, including for newcomers 2. Usable while offline 3. Usable in pure-text mode 4. Decentralized Something else? In my opinion, a central system with good Git integration (helps with 1) and both a straightforward web UI (also helps 1) and a good email interface (which gives both 2 and 3) and the ability to export the review history (which avoids lockin, the most important aspect of 4) would be perfect. Is there such a thing? Michael [1] ...probably other websites too. I'm really not trying to flog GitHub here; it's just the one I have the most experience with. In fact, I kindof assume that the Git project would choose a service that is itself based on open-source software. -- Michael Haggerty mhagger@alum.mit.edu ^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: Our cumbersome mailing list workflow 2014-11-28 15:34 ` Michael Haggerty @ 2014-11-28 16:24 ` brian m. carlson 2014-12-01 2:46 ` Junio C Hamano 1 sibling, 0 replies; 61+ messages in thread From: brian m. carlson @ 2014-11-28 16:24 UTC (permalink / raw) To: Michael Haggerty Cc: Eric Wong, Torsten Bögershausen, Junio C Hamano, Stefan Beller, Jonathan Nieder, Ronnie Sahlberg, git [-- Attachment #1: Type: text/plain, Size: 2193 bytes --] On Fri, Nov 28, 2014 at 04:34:09PM +0100, Michael Haggerty wrote: > My ideal would be to invert the procedure. Let the patches in a public > Git repository somewhere be the primary artifact, and let the review > process be focused there. Let email be an alternative interface to the > central review site: > > * Generate patch emails (similar to the current format) when pull > requests are submitted. > > * Generate notification emails when people comment on the patches. > > * Allow people to respond to the patch and notification emails via > email. The central review site should associate those comments with the > patches that they apply to, and present them along with other review > comments received via other interfaces. I think these are good goals. Even as a semi-regular contributor, I prefer to push branches around using Git rather than formatting patches and mailing them. Also, I think that being able to comment on a patch or report a bug without a login (via email) is desirable. I'm not a fan of having to have an account on every Bugzilla on the planet. That's why I like debbugs. > It seems like a few desirable features are being talked about here, and > summarizing the discussion as "centralized" vs "decentralized" is too > simplistic. What is really important? > > 1. Convenient and efficient, including for newcomers > 2. Usable while offline > 3. Usable in pure-text mode > 4. Decentralized I think 1 is definitely important. For me personally, 2 isn't very important, as all my email is via IMAP (so I have to be online). I think 3 is important for accessibility reasons. There are a lot of blind or low-sighted people for whom a GUI is infeasible or burdensome. > Something else? It might be useful to have a system that has a bug or issue tracker. We often have posts to the mailing list that don't get a response, even though those may represent legitimate bugs (code or documentation). -- brian m. carlson / brian with sandals: Houston, Texas, US +1 832 623 2791 | http://www.crustytoothpaste.net/~bmc | My opinion only OpenPGP: RSA v4 4096b: 88AC E9B2 9196 305B A994 7552 F1BA 225C 0223 B187 [-- Attachment #2: Digital signature --] [-- Type: application/pgp-signature, Size: 819 bytes --] ^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: Our cumbersome mailing list workflow 2014-11-28 15:34 ` Michael Haggerty 2014-11-28 16:24 ` brian m. carlson @ 2014-12-01 2:46 ` Junio C Hamano 2014-12-03 2:20 ` Stefan Beller 1 sibling, 1 reply; 61+ messages in thread From: Junio C Hamano @ 2014-12-01 2:46 UTC (permalink / raw) To: Michael Haggerty Cc: Eric Wong, Torsten Bögershausen, Stefan Beller, Jonathan Nieder, Ronnie Sahlberg, git Michael Haggerty <mhagger@alum.mit.edu> writes: > It seems like a few desirable features are being talked about here, and > summarizing the discussion as "centralized" vs "decentralized" is too > simplistic. What is really important? > > 1. Convenient and efficient, including for newcomers > 2. Usable while offline > 3. Usable in pure-text mode > 4. Decentralized > > Something else? As a reviewer / contributor (not speaking as the top maintainer), I would say that everything in one place, and for that one place mailbox is preferrable. "Somebody commented on (this instance of | the central) Gerrit, come look at it" is not usable; sending that comment out to those who work in their MUA, and allowing them to respond via their MUA probably adding their response as a new comment to Gerrit) would be usable. When I had to view a large-ish series by Ronnie on Gerrit, it was fairly painful. The interaction on an individual patch might be more convenient and efficient using a system like Gerrit than via e-mailed patch with reply messages, but as a vehicle to review a large series and see how the whole thing fits together, I did not find pages that made it usable (I am avoiding to say "I found it unusable", as that impression may be purely from that I couldn't find a more suitable pages that showed the same information in more usable form, i.e. user inexperience). Speaking of the "whole picture", I am hesitant to see us pushed into the "here is a central system (or here are federated systems) to handle only the patch reviews" direction; our changes result after discussing unrelated features, wishes, or bugs that happen outside of any specific patches with enough frequency, and that is why I prefer "everything in one place" aspect of the development based on the mailing list. That is not to say that the "one place" has forever to be the mailing list, though. But the tooling around an e-mail based workflow (e.g. marking threads as "worth revisiting" for later inspection, saving chosen messages into a mailbox and running "git am" on it) is already something I am used to. Whatever system we might end up migrating to, the convenience it offers has to beat the convenience of existing workflow to be worth switching to, at least to me as a reviewer/contributor. As the maintainer, I am not worried too much. As long as the mechanism can (1) reach "here is a series that is accepted by reviewers whose opinions are trusted" efficiently, and (2) allow me to queue the result without mistakes, I can go along with anything reasonable. ^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: Our cumbersome mailing list workflow 2014-12-01 2:46 ` Junio C Hamano @ 2014-12-03 2:20 ` Stefan Beller 2014-12-03 3:53 ` Jonathan Nieder 2014-12-03 17:28 ` Torsten Bögershausen 0 siblings, 2 replies; 61+ messages in thread From: Stefan Beller @ 2014-12-03 2:20 UTC (permalink / raw) To: Junio C Hamano Cc: Michael Haggerty, Eric Wong, Torsten Bögershausen, Jonathan Nieder, git@vger.kernel.org On Sun, Nov 30, 2014 at 6:46 PM, Junio C Hamano <gitster@pobox.com> wrote: > Michael Haggerty <mhagger@alum.mit.edu> writes: > >> It seems like a few desirable features are being talked about here, and >> summarizing the discussion as "centralized" vs "decentralized" is too >> simplistic. What is really important? >> >> 1. Convenient and efficient, including for newcomers >> 2. Usable while offline >> 3. Usable in pure-text mode >> 4. Decentralized >> >> Something else? > So when I started overtaking the ref log series by Ronnie, Ronnies main concern was missing reviewers time. So my idea was to make it as accessible as possible, so the reviewing party can use their time best. However here are a few points, I want to mention: * Having send emails as well as uploaded it to Gerrit, I either needed a ChangeId (Gerrit strictly requires them to track inter-patch diffs), and the mailing list here strictly avoids them, so I was told. Ok, that's my problem as I wasn't following the actual procedure of the Git development model (mailing list only). * That's why I stopped uploads to Gerrit, so I do not need to care about the ChangeIds any more. I am not sure if that improved the quality of my patches though. * I seem to not have found the right workflow with the mailing list yet, as I personally find copying around the inter-patch changelog very inconvenient. Most of the regulars here just need fewer iterations, so I can understand, that you find it less annoying. Hopefully I'll also get used to the nit-picky things and will require less review iterations in the future. How are non-regulars/newcomers, who supposingly need more iterations on a patch, supposed to handle the inter patch change log conveniently? I tried to keep the inter patch changelog be part of the commit message and then just before sending the email, I'd move it the non-permanent section of the email. * Editing patches as text files is hard/annoying. I have setup git send-email, and that works awesome, as I'd only need one command to send off a series. Having a step in between makes it more error-prone. So I do git format-patch and then inject the inter patch change log, check to remove ChangeId and then use git send-email. And at that final manual step I realized I am far from being perfect, so sometimes patches arrive on the mailing list, which are sub quality in the sense, that there are leftovers, i.e. a ChangeId * A possible feature, which just comes to my mind: Would it make sense for format-patch to not just show the diff stats, but also include, on which branch it applies? In git.git this is usually the origin/master branch, but dealing with patch series, building on top of each other that may be a good feature to have. > > When I had to view a large-ish series by Ronnie on Gerrit, it was > fairly painful. The interaction on an individual patch might be > more convenient and efficient using a system like Gerrit than via > e-mailed patch with reply messages, but as a vehicle to review a > large series and see how the whole thing fits together, I did not > find pages that made it usable (I am avoiding to say "I found it > unusable", as that impression may be purely from that I couldn't > find a more suitable pages that showed the same information in more > usable form, i.e. user inexperience). So you're liking the email workflow more. How do you do the final formatting of an email, such as including the inter patch diff? > > Speaking of the "whole picture", I am hesitant to see us pushed into > the "here is a central system (or here are federated systems) to > handle only the patch reviews" direction; our changes result after > discussing unrelated features, wishes, or bugs that happen outside > of any specific patches with enough frequency, and that is why I > prefer "everything in one place" aspect of the development based on > the mailing list. That is not to say that the "one place" has > forever to be the mailing list, though. But the tooling around an > e-mail based workflow (e.g. marking threads as "worth revisiting" > for later inspection, saving chosen messages into a mailbox and > running "git am" on it) is already something I am used to. Whatever > system we might end up migrating to, the convenience it offers has > to beat the convenience of existing workflow to be worth switching > to, at least to me as a reviewer/contributor. I do like the way as well to just mark emails unread when I need to work on them later. > > As the maintainer, I am not worried too much. As long as the > mechanism can (1) reach "here is a series that is accepted by > reviewers whose opinions are trusted" efficiently, and (2) allow > me to queue the result without mistakes, I can go along with > anything reasonable. > ^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: Our cumbersome mailing list workflow 2014-12-03 2:20 ` Stefan Beller @ 2014-12-03 3:53 ` Jonathan Nieder 2014-12-03 17:18 ` Junio C Hamano 2014-12-03 17:28 ` Torsten Bögershausen 1 sibling, 1 reply; 61+ messages in thread From: Jonathan Nieder @ 2014-12-03 3:53 UTC (permalink / raw) To: Stefan Beller Cc: Junio C Hamano, Michael Haggerty, Eric Wong, Torsten Bögershausen, git@vger.kernel.org Stefan Beller wrote: > How are non-regulars/newcomers, who supposingly need more iterations on > a patch, supposed to handle the inter patch change log conveniently? I think this is one of the more important issues. I don't think there's any reason that newcomers should need more iterations than regulars to finish a patch. Regulars are actually held to a higher standard, so they are likely to need more iterations. A common mistake for newcomers, that I haven't learned yet how to warn properly against, is to keep re-sending minor iterations on a patch too quickly. Some ways to avoid that: * feel free to respond to review comments with something like "how about this?" and a copy/pasted block of code that just addresses that one comment. That way, you can clear up ambiguity and avoid the work of applying that change to the entire patch if it ends up seeming like a bad idea. This also avoids having to re-send a larger patch or series multiple times to clear up a small ambiguity from a review. * be proactive. Look for other examples of the same issue that a reviewer pointed out once so they don't have to find it again elsewhere in the next iteration. Run the testsuite. Build with the flags from https://kernel.googlesource.com/pub/scm/git/git/+/todo/Make#106 in CFLAGS in config.mak. Proofread and try to read as though you knew nothing about the patch to anticipate what reviewers will find. * feel free to get more review out-of-band, too. If you're still playing with ideas and want someone to take a quick glance before the patches are in reviewable form, you can do that and say so (e.g., with 'RFC/' before 'PATCH' in the subject line). Jonathan ^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: Our cumbersome mailing list workflow 2014-12-03 3:53 ` Jonathan Nieder @ 2014-12-03 17:18 ` Junio C Hamano 0 siblings, 0 replies; 61+ messages in thread From: Junio C Hamano @ 2014-12-03 17:18 UTC (permalink / raw) To: Jonathan Nieder Cc: Stefan Beller, Michael Haggerty, Eric Wong, Torsten Bögershausen, git@vger.kernel.org Jonathan Nieder <jrnieder@gmail.com> writes: > I don't think there's any reason that newcomers should need more > iterations than regulars to finish a patch. Regulars are actually > held to a higher standard, so they are likely to need more iterations. > > A common mistake for newcomers, that I haven't learned yet how to warn > properly against, is to keep re-sending minor iterations on a patch > too quickly. Some ways to avoid that: > > * feel free to respond to review comments with something like "how > about this?" and a copy/pasted block of code that just addresses > that one comment. That way, you can clear up ambiguity and avoid > the work of applying that change to the entire patch if it ends > up seeming like a bad idea. This also avoids having to re-send a > larger patch or series multiple times to clear up a small ambiguity > from a review. This can go both ways. A trivial improvement can be suggested that way by the reviewer. > * be proactive. Look for other examples of the same issue that a > reviewer pointed out once so they don't have to find it again > elsewhere in the next iteration.... > * feel free to get more review out-of-band, too. If you're still > playing with ideas and want someone to take a quick glance before > the patches are in reviewable form, you can do that and say so > (e.g., with 'RFC/' before 'PATCH' in the subject line). Overall, good suggestions. Thanks. ^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: Our cumbersome mailing list workflow 2014-12-03 2:20 ` Stefan Beller 2014-12-03 3:53 ` Jonathan Nieder @ 2014-12-03 17:28 ` Torsten Bögershausen 1 sibling, 0 replies; 61+ messages in thread From: Torsten Bögershausen @ 2014-12-03 17:28 UTC (permalink / raw) To: Stefan Beller, Junio C Hamano Cc: Michael Haggerty, Eric Wong, Jonathan Nieder, git@vger.kernel.org On 2014-12-03 03.20, Stefan Beller wrote: > On Sun, Nov 30, 2014 at 6:46 PM, Junio C Hamano <gitster@pobox.com> wrote: >> Michael Haggerty <mhagger@alum.mit.edu> writes: >> >>> It seems like a few desirable features are being talked about here, and >>> summarizing the discussion as "centralized" vs "decentralized" is too >>> simplistic. What is really important? >>> >>> 1. Convenient and efficient, including for newcomers >>> 2. Usable while offline >>> 3. Usable in pure-text mode >>> 4. Decentralized >>> >>> Something else? > So when I started overtaking the ref log series by Ronnie, > Ronnies main concern was missing reviewers time. So my idea was to > make it as accessible as possible, so the reviewing party can use their > time best. However here are a few points, I want to mention: > > * Having send emails as well as uploaded it to Gerrit, I either needed > a ChangeId (Gerrit strictly requires them to track inter-patch > diffs), and the > mailing list here strictly avoids them, so I was told. > Ok, that's my problem as I wasn't following the actual procedure of the > Git development model (mailing list only). > * That's why I stopped uploads to Gerrit, so I do not need to care about the > ChangeIds any more. I am not sure if that improved the quality of my patches > though. > * I seem to not have found the right workflow with the mailing list yet, as I > personally find copying around the inter-patch changelog very inconvenient. > Most of the regulars here just need fewer iterations, so I can understand, > that you find it less annoying. Hopefully I'll also get used to the > nit-picky things > and will require less review iterations in the future. > How are non-regulars/newcomers, who supposingly need more iterations on > a patch, supposed to handle the inter patch change log conveniently? > I tried to keep the inter patch changelog be part of the commit message and > then just before sending the email, I'd move it the non-permanent section of > the email. > * Editing patches as text files is hard/annoying. Not sure if I understand. Editing text files isn't that hard, we do it all the time. > I have setup git send-email, > and that works awesome, as I'd only need one command to send off a series. > Having a step in between makes it more error-prone. So I do git format-patch > and then inject the inter patch change log, check to remove ChangeId and then > use git send-email. How do you "inject the inter patch change log" ? Is that manually, or is it a script ? > And at that final manual step I realized I am > far from being > perfect, so sometimes patches arrive on the mailing list, which are > sub quality > in the sense, that there are leftovers, i.e. a ChangeId > * A possible feature, which just comes to my mind: > Would it make sense for format-patch to not just show the diff > stats, but also > include, on which branch it applies? In git.git this is usually the > origin/master > branch, but dealing with patch series, building on top of each other that may > be a good feature to have. > Thanks for the description (and everybody for the discussion) In the hope that it may help, I can try to describe my work flow: - Run a script to send the patch (this is a real example) ################# SRCCOMMIT=119efe90bffee688a3c37d4358667 DSTCOMMIT=$(git log --oneline -n1 | awk '{print $1}') VERSION="-v 1" PATCHFILE=$( echo $0 | sed -e 's/\.sh$/.patch/') GIT_TEST_LONG=t export GIT_TEST_LONG git am --abort || : ( test -s $PATCHFILE || git format-patch $VERSION -s --to=git@vger.kernel.org --cc=tboegi@web.de --cc=mhagger@alum.mit.edu --stdout $SRCCOMMIT..$DSTCOMMIT >$PATCHFILE ) && git checkout $SRCCOMMIT && git am <$PATCHFILE && cd t && cd .. && make && (cd t && ./t0001*.sh) && git imap-send <$PATCHFILE ##################### The script formats a patch file (if that does not exist), applies the patch on the source commit, runs make and then the test cases to verify that the patch works. (For bigger patches more tests or the whole test suite should be run, for this very isolated work it OK to run a singe test) Once everything is OK, the patch is stored both on disc and in the Drafts folder of the "email program". (In your case you can use grep to remove the ChangedId or to check that it had been removed) Now it is time to "tweak" the patch file with an editor: Add what has been changed since V1.... Save the patch file, run the script again to verify that the patch still applies and works and put it into the Drafts folder of the mail program. (That's why I abort the "git imap-send" in the first round and press ^C when the password is asked) Start the favorite email program (Kmail works, or Thunderbird or every other program that can send email in "plain text") Have a final look at the patch in the email prgram (remove the V1 from the header, change PATCH into PATCH/RFC). Let the spell checker look at it, re-read once more. If everything is OK, press the "send" button. If I send out a V2 version, make a copy of the script, and call it doit2.sh, change what needs to be changed. We can enhance the script to push to a global repo, create a new branch just to be sure we re-find our work... I store all these scripts under a folder in my home directory, each script has it's own directory, this for example is under 141119_check_file_mode_for_SAMBA/. And if I am afraid that I don't know where it ended, I can make a comment file here and notice that Junio picked it up here: junio/tb/config-core-filemode-check-on-broken-fs (And the remote junio is "git://github.com/gitster/git.git") The good thing is that both the script and the patch file can be put under version control. I realized that re-checking the email which is rally send out to the list is worth the time and effort. Sometimes I keep it in the Drafts folder over night, and have a new look with fresh eyes the next day. ^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: Our cumbersome mailing list workflow 2014-11-27 17:46 ` Our cumbersome mailing list workflow Torsten Bögershausen 2014-11-27 18:24 ` Matthieu Moy 2014-11-27 22:53 ` Eric Wong @ 2014-11-28 14:31 ` Michael Haggerty 2014-11-28 15:42 ` Marc Branchaud 2 siblings, 1 reply; 61+ messages in thread From: Michael Haggerty @ 2014-11-28 14:31 UTC (permalink / raw) To: Torsten Bögershausen, Junio C Hamano Cc: Stefan Beller, Jonathan Nieder, Ronnie Sahlberg, git On 11/27/2014 06:46 PM, Torsten Bögershausen wrote: > On 2014-11-25 01.28, Michael Haggerty wrote: > [] >> Let me list the aspects of our mailing list workflow that I find >> cumbersome as a contributor and reviewer: >> >> * Submitting patches to the mailing list is an ordeal of configuring >> format-patch and send-email and getting everything just right, using >> instructions that depend on the local environment. > Typically everything fits into ~/.gitconfig, > which can be carried around on a USB-Stick. > Is there any details which I miss, or howtows we can improve ? I used to need one setup at work and a different one at home (because of how my email was configured), and sometimes had to switch back and forth as I carried my notebook around. >> [...] >> * I do "git fetch gitster", then try to figure out whether the branch >> I'm interested in is present, what its name is, and whether the version >> in your tree is the latest version, then "git checkout xy/foobar". > There are 12 branches from mh/, so it should be possible to find the name, > und run git log gitster/xy/fix_this_bug or so. > Even more important, this branch is the "single point of truth", because > this branch may be merged eventually, and nothing else. I know it's *possible*. The question is whether it could be made easier. >> * Following patch series across iterations is also awkward. To compare >> two versions, I have to first get both patch series into my repo, which >> involves digging through the ML history to find older versions, followed >> by the "git am" steps. Often submitters are nice enough to put links to >> previous versions of their patch series in their cover letters, but the >> links are to a web-based email archive, from which it is even more >> awkward to grab and apply patches. So in practice I then go back to my >> email client and search my local archive for my copy of the same email >> that was referenced in the archive, and apply the patch from there. >> Finding comments about old versions of a patch series is nearly as much >> work. > In short: > We can ask every contributor, if the patch send to the mailing list > is available on a public Git-repo, and what the branch name is, > like _V2.. Does this makes sense ? That would be helpful, but it would put yet *another* requirement on the submitter (to send patch emails *and* push the branch to some accessible repository). We regulars could script this pretty easily, but people who only contribute occasionally or who are trying to get started will be even more overwhelmed. > As an alternative, you can save the branches locally, after running > git-am once, just keep the branch. > [] Yes, but it is even more unnecessary manual bookkeeping. > [...] > But there is another thing: > Once a patch is send out, I would ask the sender to wait and collect comments > at least 24 hours before sending a V2. > We all living in different time zones, so please let the world spin once. Yes, good idea. > My feeling is that a patch > 5 commits should have > a waiting time > 5 days, otherwise I start reviewing V1, then V2 comes, > then V3 before I am finished with V1. That is not ideal. One day per patch might be exaggerated, but I agree that long series should be iterated more slowly than short ones. > What does it cost to push your branch to a public repo and > include that information in the email ? One has to run an additional command and add some information to the cover letter, every time a patch series is submitted. If it's scripted then it's relatively painless. But for a newcomer these will be manual steps that are easy to forget or to do incorrectly, making it more likely that the newcomer's first contribution to Git will end in mild embarrassment rather than success. Michael -- Michael Haggerty mhagger@alum.mit.edu ^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: Our cumbersome mailing list workflow 2014-11-28 14:31 ` Michael Haggerty @ 2014-11-28 15:42 ` Marc Branchaud 2014-11-28 21:39 ` Damien Robert 0 siblings, 1 reply; 61+ messages in thread From: Marc Branchaud @ 2014-11-28 15:42 UTC (permalink / raw) To: Michael Haggerty, Torsten Bögershausen, Junio C Hamano Cc: Stefan Beller, Jonathan Nieder, Ronnie Sahlberg, git On 14-11-28 09:31 AM, Michael Haggerty wrote: > On 11/27/2014 06:46 PM, Torsten Bögershausen wrote: >> On 2014-11-25 01.28, Michael Haggerty wrote: >> [] >>> Let me list the aspects of our mailing list workflow that I find >>> cumbersome as a contributor and reviewer: >>> >>> * Submitting patches to the mailing list is an ordeal of configuring >>> format-patch and send-email and getting everything just right, using >>> instructions that depend on the local environment. >> Typically everything fits into ~/.gitconfig, >> which can be carried around on a USB-Stick. >> Is there any details which I miss, or howtows we can improve ? > > I used to need one setup at work and a different one at home (because of > how my email was configured), and sometimes had to switch back and forth > as I carried my notebook around. > >>> [...] >>> * I do "git fetch gitster", then try to figure out whether the branch >>> I'm interested in is present, what its name is, and whether the version >>> in your tree is the latest version, then "git checkout xy/foobar". >> There are 12 branches from mh/, so it should be possible to find the name, >> und run git log gitster/xy/fix_this_bug or so. >> Even more important, this branch is the "single point of truth", because >> this branch may be merged eventually, and nothing else. > > I know it's *possible*. The question is whether it could be made easier. > >>> * Following patch series across iterations is also awkward. To compare >>> two versions, I have to first get both patch series into my repo, which >>> involves digging through the ML history to find older versions, followed >>> by the "git am" steps. Often submitters are nice enough to put links to >>> previous versions of their patch series in their cover letters, but the >>> links are to a web-based email archive, from which it is even more >>> awkward to grab and apply patches. So in practice I then go back to my >>> email client and search my local archive for my copy of the same email >>> that was referenced in the archive, and apply the patch from there. >>> Finding comments about old versions of a patch series is nearly as much >>> work. >> In short: >> We can ask every contributor, if the patch send to the mailing list >> is available on a public Git-repo, and what the branch name is, >> like _V2.. Does this makes sense ? > > That would be helpful, but it would put yet *another* requirement on the > submitter (to send patch emails *and* push the branch to some accessible > repository). We regulars could script this pretty easily, but people who > only contribute occasionally or who are trying to get started will be > even more overwhelmed. A bot could subscribe to the list and create branches in a public repo. (This idea feels familiar -- didn't somebody attempt this already?) Integrate the bot into the list manager, and every PATCH email sent through the list could have the patch's URL (maybe in the footer, or as an X- header). Could this make a decent GSoC project? M. ^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: Our cumbersome mailing list workflow 2014-11-28 15:42 ` Marc Branchaud @ 2014-11-28 21:39 ` Damien Robert 0 siblings, 0 replies; 61+ messages in thread From: Damien Robert @ 2014-11-28 21:39 UTC (permalink / raw) To: git > A bot could subscribe to the list and create branches in a public repo. > (This idea feels familiar -- didn't somebody attempt this already?) Thomas Rast maintains git notes that link git commits to their gmane discussion, you can get them with [remote "mailnotes"] url = git://github.com/trast/git.git fetch = refs/heads/notes/*:refs/notes/* There is gmane branch and a message-id branch, its pretty usefull. ^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: Our cumbersome mailing list workflow 2014-11-25 0:28 ` Our cumbersome mailing list workflow (was: Re: [PATCH 0/6] repack_without_refs(): convert to string_list) Michael Haggerty 2014-11-27 17:46 ` Our cumbersome mailing list workflow Torsten Bögershausen @ 2014-12-03 23:57 ` Philip Oakley 2014-12-04 2:03 ` Stefan Beller 1 sibling, 1 reply; 61+ messages in thread From: Philip Oakley @ 2014-12-03 23:57 UTC (permalink / raw) To: Michael Haggerty, Junio C Hamano Cc: Stefan Beller, Jonathan Nieder, Ronnie Sahlberg, git From: "Michael Haggerty" <mhagger@alum.mit.edu> Sent: Tuesday, November 25, 2014 12:28 AM > On 11/21/2014 07:00 PM, Junio C Hamano wrote: >> Michael Haggerty <mhagger@alum.mit.edu> writes: >> >>> I don't think that those iterations changed anything substantial >>> that >>> overlaps with my version, but TBH it's such a pain in the ass >>> working >>> with patches in email that I don't think I'll go to the effort of >>> checking for sure unless somebody shows interest in actually using >>> my >>> version. >>> >>> Sorry for being grumpy today :-( [..] > Let me list the aspects of our mailing list workflow that I find > cumbersome as a contributor and reviewer: > > * Submitting patches to the mailing list is an ordeal of configuring > format-patch and send-email and getting everything just right, using > instructions that depend on the local environment. We saw that hardly > any GSoC applicants were able to get it right on their first attempt. > Submitting a patch series should be as simple as "git push". > > * Once patches are submitted, there is no assurance that you (Junio) > will apply them to your tree at the same point that the submitter > developed and tested them. > > * The branch name that you choose for a patch series is not easily > derivable from the patches as they appeared in the mailing list. > Trying > to figure out whether/where the patches exist in your tree is a > largely > manual task. The reverse mapping, from in-tree commit to the email > where > it was proposed, is even more difficult to infer. > > * Your tree has no indication of which version of a patch series (v1, > v2, etc) is currently applied. > > The previous three points combine to make it awkward to get patches > into > my local repository to review or test. There are two alternatives, > both > cumbersome and imprecise: > > * I do "git fetch gitster", then try to figure out whether the branch > I'm interested in is present, what its name is, and whether the > version > in your tree is the latest version, then "git checkout xy/foobar". > I had a thought about the issue of version labeling and of keeping the old patch series hanging about during development that I felt was worth recording. My thought was that while the cover letter and series version number are currently stripped out from the start of the series, they could be added back as a supplemental commit at the end of the series (an --allow-empty commit). This could contain all of the patch subject lines and their post '---' notes as appropriate. Thus the series branch would appear to have an extra commit (compared to the current process) after the original tip's possible merge into say pu. When subsequent series are sent to the list, the new supplemental commit would be a 'merge', with its second parent being the old series, thus the old series is not lost until the branch is deleted, and the existing merge pattern is retained. Clearly if this would need some additional coding as it's not suitable as a manual process, but it could be just as automatic as the current process while providing that little bit of additional visibility. Below, I've tried to set out how the commit graph might look (oldest to the left). Hopefully my MUA won't ruin it. The first patch series branches at A, and is merged at D, with the supplemental commit labeled with v1z. When the new series arrives, and pu is rewound, we have the new series applied from G (which in reality may not be linked directly from A), and merged back at K. However the new v2z supplemental commit is now the po/patches branch head, and is also a merge back to v1z. patch series 1 (cover letter z) - A - B - C - D - E - F <- pu \ / v1a-v1b--v1z <-po/patches patch series 2 - A - G - H - I - J - K <- pu (note re-wound) | \ / (merge D lost) \ v2a-v2b-v2c--v2z <-po/patches \ / v1a-v1b--v1z - - -. The key idea here is to use the existing branching model, but then to add the cover letter and other details at the end, rather than the beginning as might have been expected from the email transmit sequence. Philip ^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: Our cumbersome mailing list workflow 2014-12-03 23:57 ` Philip Oakley @ 2014-12-04 2:03 ` Stefan Beller 0 siblings, 0 replies; 61+ messages in thread From: Stefan Beller @ 2014-12-04 2:03 UTC (permalink / raw) To: Philip Oakley Cc: Michael Haggerty, Junio C Hamano, Jonathan Nieder, Ronnie Sahlberg, git@vger.kernel.org > Editing text files isn't that hard, we do it all the time. It is not indeed. But doing it all over again and again is hard and error prone. I did re-read the man page on git format-patch and found the --notes option, which I am going to try to use in my workflow. That way I only need to update the notes instead of redoing them all the time. By redoing it I mean copying the changelog from the last time I sent the patch and adding new entries. > My thought was that while the cover letter and series version number are currently stripped out from the start of the series, they could be added back as a supplemental commit at the end of the series (an --allow-empty commit). This could contain all of the patch subject lines and their post '---' notes as appropriate. This sounds interesting. The only changes I can see here are the referenced message ids, so it would be worthwhile to have the last patch sent out first and all other patches 1..n-1 referencing the last empty commit. If additionally the numbering is corrected, the reader of the mailing list would not notice any difference to the status quo, just the sender would have the convenience to be able to track the cover letter as an empty commit on top of a series. ^ permalink raw reply [flat|nested] 61+ messages in thread
end of thread, other threads:[~2014-12-04 2:03 UTC | newest] Thread overview: 61+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2014-11-18 22:43 [PATCH] refs.c: use a stringlist for repack_without_refs Stefan Beller 2014-11-18 23:06 ` Junio C Hamano 2014-11-18 23:39 ` Junio C Hamano 2014-11-18 23:45 ` Jonathan Nieder 2014-11-19 0:28 ` Stefan Beller 2014-11-19 1:08 ` Stefan Beller 2014-11-19 18:00 ` Junio C Hamano 2014-11-19 18:50 ` Stefan Beller 2014-11-19 20:44 ` Jonathan Nieder 2014-11-19 21:54 ` Stefan Beller 2014-11-19 21:59 ` [PATCH v4] " Stefan Beller 2014-11-20 2:15 ` Jonathan Nieder 2014-11-20 16:47 ` Junio C Hamano 2014-11-20 18:04 ` [PATCH v5 1/1] " Stefan Beller 2014-11-20 18:10 ` [PATCH] refs.c: repack_without_refs may be called without error string buffer Stefan Beller 2014-11-20 18:15 ` Ronnie Sahlberg 2014-11-20 18:35 ` Jonathan Nieder 2014-11-20 18:36 ` Ronnie Sahlberg 2014-11-20 18:56 ` Stefan Beller 2014-11-20 18:29 ` [PATCH v5 1/1] refs.c: use a stringlist for repack_without_refs Jonathan Nieder 2014-11-20 18:37 ` Jonathan Nieder 2014-11-20 19:01 ` Junio C Hamano 2014-11-20 19:05 ` Stefan Beller 2014-11-20 20:07 ` [PATCH v6] refs.c: use a string_list " Stefan Beller 2014-11-20 20:36 ` Jonathan Nieder 2014-11-21 14:09 ` [PATCH 0/6] repack_without_refs(): convert to string_list Michael Haggerty 2014-11-21 14:09 ` [PATCH 1/6] prune_remote(): exit early if there are no stale references Michael Haggerty 2014-11-22 21:07 ` Jonathan Nieder 2014-11-21 14:09 ` [PATCH 2/6] prune_remote(): initialize both delete_refs lists in a single loop Michael Haggerty 2014-11-21 14:09 ` [PATCH 3/6] prune_remote(): sort delete_refs_list references en masse Michael Haggerty 2014-11-21 16:44 ` Junio C Hamano 2014-11-25 7:21 ` Michael Haggerty 2014-11-25 8:04 ` Michael Haggerty 2014-11-22 21:08 ` Jonathan Nieder 2014-11-21 14:09 ` [PATCH 4/6] repack_without_refs(): make the refnames argument a string_list Michael Haggerty 2014-11-22 21:17 ` Jonathan Nieder 2014-11-25 7:42 ` Michael Haggerty 2014-11-21 14:09 ` [PATCH 5/6] prune_remote(): rename local variable Michael Haggerty 2014-11-22 21:18 ` Jonathan Nieder 2014-11-21 14:09 ` [PATCH 6/6] prune_remote(): iterate using for_each_string_list_item() Michael Haggerty 2014-11-22 21:19 ` Jonathan Nieder 2014-11-21 14:25 ` [PATCH 0/6] repack_without_refs(): convert to string_list Michael Haggerty 2014-11-21 18:00 ` Junio C Hamano 2014-11-21 19:57 ` Stefan Beller 2014-11-25 0:28 ` Our cumbersome mailing list workflow (was: Re: [PATCH 0/6] repack_without_refs(): convert to string_list) Michael Haggerty 2014-11-27 17:46 ` Our cumbersome mailing list workflow Torsten Bögershausen 2014-11-27 18:24 ` Matthieu Moy 2014-11-28 12:09 ` Philip Oakley 2014-11-27 22:53 ` Eric Wong 2014-11-28 15:34 ` Michael Haggerty 2014-11-28 16:24 ` brian m. carlson 2014-12-01 2:46 ` Junio C Hamano 2014-12-03 2:20 ` Stefan Beller 2014-12-03 3:53 ` Jonathan Nieder 2014-12-03 17:18 ` Junio C Hamano 2014-12-03 17:28 ` Torsten Bögershausen 2014-11-28 14:31 ` Michael Haggerty 2014-11-28 15:42 ` Marc Branchaud 2014-11-28 21:39 ` Damien Robert 2014-12-03 23:57 ` Philip Oakley 2014-12-04 2:03 ` Stefan Beller
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).