git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
From: Ronnie Sahlberg <sahlberg@google.com>
To: Junio C Hamano <gitster@pobox.com>
Cc: Jonathan Nieder <jrnieder@gmail.com>,
	"git@vger.kernel.org" <git@vger.kernel.org>,
	Michael Haggerty <mhagger@alum.mit.edu>
Subject: Re: [PATCH 19/24] refs.c: allow listing and deleting badly named refs
Date: Fri, 3 Oct 2014 13:32:43 -0700	[thread overview]
Message-ID: <CAL=YDWmV89jwWqru5dgxCKpx-JYCrH=vYVLT_yPqsT9Ep9Qa8Q@mail.gmail.com> (raw)
In-Reply-To: <CAL=YDW=N1+XS+U=Vs0Sba0UqdN7+HRg5CTdZB28h5Ts3_yusYg@mail.gmail.com>

On Fri, Oct 3, 2014 at 1:25 PM, Ronnie Sahlberg <sahlberg@google.com> wrote:
> On Thu, Oct 2, 2014 at 11:55 AM, Junio C Hamano <gitster@pobox.com> wrote:
>> Jonathan Nieder <jrnieder@gmail.com> writes:
>>
>>> From: Ronnie Sahlberg <sahlberg@google.com>
>>> ...
>>> In resolving functions, refuse to resolve refs that don't pass the
>>> check-ref-format(1) check unless the new RESOLVE_REF_ALLOW_BAD_NAME
>>> flag is passed.  Even with RESOLVE_REF_ALLOW_BAD_NAME, refuse to
>>> resolve refs that escape the refs/ directory and do not match the
>>> pattern [A-Z_]* (think "HEAD" and "MERGE_HEAD").
>>>
>>> In locking functions, refuse to act on badly named refs unless they
>>> are being deleted and either are in the refs/ directory or match [A-Z_]*.
>>>
>>> Just like other invalid refs, flag resolved, badly named refs with the
>>> REF_ISBROKEN flag, treat them as resolving to null_sha1, and skip them
>>> in all iteration functions except for for_each_rawref.
>>>
>>> Flag badly named refs with a REF_BAD_NAME flag to make it easier for
>>> future callers to notice and handle them specially.
>>>
>>> In the transaction API, refuse to create or update badly named refs,
>>> but allow deleting them (unless they escape refs/ and don't match
>>> [A-Z_]*).
>>>
>>> Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
>>> Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
>>
>> Thanks.  We originally threw all the different kind of breakages
>> into ISBROKEN, but a ref can have a malformed name or can contain a
>> bad/non value and allowing us to tell them apart is a good direction
>> to go.
>>
>>> diff --git a/cache.h b/cache.h
>>> index 5ca7f2b..0c0ac60 100644
>>> --- a/cache.h
>>> +++ b/cache.h
>>> @@ -978,16 +978,26 @@ extern int read_ref(const char *refname, unsigned char *sha1);
>>>   * If flags is non-NULL, set the value that it points to the
>>>   * combination of REF_ISPACKED (if the reference was found among the
>>>   * packed references), REF_ISSYMREF (if the initial reference was a
>>> - * symbolic reference) and REF_ISBROKEN (if the ref is malformed).
>>> + * symbolic reference), REF_BAD_NAME (if the reference name is ill
>>> + * formed --- see RESOLVE_REF_ALLOW_BAD_NAME below), and REF_ISBROKEN
>>> + * (if the ref is malformed).
>>
>> You want to define "is malformed" here.
>>
>> The original defines REF_ISBROKEN as "malformed" because
>>
>>  (1) resolve_ref_unsafe() uses get_sha1_hex() and read_loose_refs()
>>      uses read_ref_full(), both to catch "malformed values" stored;
>>
>>  (2) resolve_ref_unsafe() uses check_refname_format() and catches
>>      "malformed names" stored as a symref target.
>>
>> I _think_ you are introducing ALLOW_BAD_NAME to allow add the third
>> class ".git/refs/remotes/origin/mal..formed..name".  I do not know
>> if they should be the same class as a symref with a good name
>> ".git/refs/remotes/origin/HEAD" that points at a bad name
>> "mal..formed..name", which is (2) above).  Perhaps not.  (2) is
>> still above what is stored in the ref, and the ref in question may
>> or may not have a well-formed name, which is orthogonal.
>>
>> So probably you left only the "value stored in the ref is malformed"
>> (in other words, "we expected to find 40-hex object name but didn't
>> find one") case for REF_ISBROKEN?
>
> I updated cache.h to try to clarify it better.
> The intention here is to expand the use of REF_ISBROKEN.
>
> For all cases  REF_ISBROKEN will be set. This includes both "the sha1
> value is bad" as well as "a name is bad".
>
> For those cases where a name is bad then REF_BAD_NAME is also set. Bad
> names are when either the ref itself has a bad name or when a bad name
> is encountered while resolving a chain of symbilic refs.
>
>
> I.e.  REF_BAD_NAME is a special case of broken ref. All REF_BAD_NAME
> refs are also REF_ISBROKEN but not the reverse.

Let me add some rationale why REF_BAD_NAME and REF_ISBROKEN are
notorthogonal properties.

I think almost all callers of these APIs are only concerned about "is
the ref good or bad" and they today only check REF_ISBROKEN.
I think that is a reasonable API and it allows the majority of callers
to just "check this single flag".
(The alternative would be to keep all callers in sync and use a set of
flags for all "bad conditions".)

A very small subset of callers are actually interested in knowing why
the ref was bad, and in particular if the ref was bad due to a name
component.
Those callers, that are aware that there are different types of
ISBROKEN can then inspect the REF_BAD_NAME flag in order to decide
"is the ref broken due to the ref name?".


This is why I made REF_BAD_NAME a special case of REF_ISBROKEN.



>
>
>>
>> Do we want to separate "value is not 40-hex" and "a symref points at
>> a malformed refname" as separate "malformed value" errors?
>>
>>> + * RESOLVE_REF_ALLOW_BAD_NAME allows resolving refs even when their
>>> + * name is invalid according to git-check-ref-format(1).  If the name
>>> + * is bad then the value stored in sha1 will be null_sha1 and the
>>> + * REF_ISBROKEN and REF_BAD_NAME flags will be set.
>>
>> Viewed with that light, I am not sure if a badly named ref should
>> yield null_sha1[] (REF_ISBROKEN, which I am assuming is about a
>> value that is badly formatted and cannot be read, should keep
>> yielding it as before).  Wouldn't it make it harder for the user if
>> you give null_sha1[] back to somebody who is trying to recover by
>> reading "refs/heads/mal..formed", creating "refs/heads/sensible" to
>> point at the same value and then removing the former?
>>
>> Note that I am not saying we should give back the parsed value at
>> this step in the series.  Perhaps there are some existing callers
>> that do not check for ISBROKEN flag and instead says "null_sha1[]
>> ref is to be rejected/ignored", in which case they may need to be
>> corrected before that happens.  Or there may be some reason I
>> overlooked that makes it not so useful if we returned the object
>> name stored in a ref whose name is malformed.  Just wondering.
>
> The reason for these malformed refs resolving to null_sha1 is exactly
> that. There may be callers that do not check ISBROKEN, so all those
> callers need to be carefully audited before we start returning
> potentially valid non null_sha1 here.
> Right now I only want to do the minimal changes needed to open up only
> those few paths I need to in order to allow the refs to be deleted.
> For the delete case, returning the null_sha1 is sufficient.
>
>
> Now, IF we add support to "rename a bad-ref-name to a good-ref-name",
> then for that case we would need to start allowing resolve_ref_unsafe
> to actually return the sha1 for the ref.
> And that would require that we do those changes and audits. Given how
> rare this should be, I am not convinced we need "rename bad to good"
> at this point and would thus prefer to hold off adding that.
>
> Thus, returning the object name is not so useful for the problem I try
> to solve at this stage, namely "make delete work".
>
>
>>
>>> @@ -272,6 +272,37 @@ static struct ref_dir *get_ref_dir(struct ref_entry *entry)
>>>       return dir;
>>>  }
>>>
>>> +static int escapes_cwd(const char *path) {
>>> +     char *buf;
>>> +     int result;
>>> +
>>> +     if (is_absolute_path(path))
>>> +             return 1;
>>> +     buf = xmalloc(strlen(path) + 1);
>>> +     result = !!normalize_path_copy(buf, path);
>>> +     free(buf);
>>> +     return result;
>>> +}
>>
>> I think this function is misnamed for two reasons.
>>
>>  - It does not have anything to do with cwd; it does not make any
>>    difference to the outcome of this function given the same input,
>>    if 'pwd' says "/u/jc/git" or "/u/jc/git/Documentation", no?
>>
>>  - Even if this had something to do with cwd, I would expect a
>>    function whose name is escapes_cwd("/u/jc/git/Documentation") to
>>    yield false when 'pwd' says "/u/jc/git", but the implementation
>>    unconditionally rejects absolute path.  In the context of the
>>    (sole) caller of this function, which deals with a refname "refs/...",
>>    it makes no sense to see an absolute path, but that does not have
>>    anything to do with "does this path escape cwd?", no?
>>
>
> Agree.
> I removed this function completely and just inline the !normalize_path_copy()
> checks to make sure that refs/* paths remain within refs/.
>
>
>>> +/*
>>> + * Check if a refname is safe.
>>> + * For refs that start with "refs/" we consider it safe as long as the rest
>>> + * of the path components does not allow it to escape from this directory.
>>> + * For all other refs we only consider them safe iff they only contain
>>> + * upper case characters and '_'.
>>> + */
>>
>> I presume that the exception is to accomodate for "HEAD", "ORIG_HEAD",
>> "MERGE_HEAD" and friends, but you probably do not want the readers to
>> guess.
>>
>
> I updated the commend to highlight that this is for "HEAD" and friends.
>
>>> +static int refname_is_safe(const char *refname)
>>> +{
>>> +     if (starts_with(refname, "refs/"))
>>> +             return !escapes_cwd(refname + strlen("refs/"));
>>> +     while (*refname) {
>>> +             if (!isupper(*refname) && *refname != '_')
>>> +                     return 0;
>>> +             refname++;
>>> +     }
>>> +     return 1;
>>> +}

  reply	other threads:[~2014-10-03 20:32 UTC|newest]

Thread overview: 129+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-07-30 17:10 Transaction patch series overview Ronnie Sahlberg
2014-07-31 21:41 ` Ronnie Sahlberg
2014-08-08 16:50   ` Ronnie Sahlberg
2014-08-19 19:54     ` Ronnie Sahlberg
2014-08-19 22:28       ` Junio C Hamano
2014-08-20 23:17       ` Jonathan Nieder
2014-08-26  0:03         ` Jonathan Nieder
2014-08-26 21:01           ` Junio C Hamano
2014-08-26 22:14             ` Jonathan Nieder
2014-08-27  0:28               ` [PATCH 0/20] rs/ref-transaction-1 (Re: Transaction patch series overview) Jonathan Nieder
2014-08-27  0:29                 ` [PATCH 01/20] refs.c: change ref_transaction_create to do error checking and return status Jonathan Nieder
2014-08-27  0:29                 ` [PATCH 02/20] refs.c: update ref_transaction_delete to check for error " Jonathan Nieder
2014-08-27  0:30                 ` [PATCH 03/20] refs.c: make ref_transaction_begin take an err argument Jonathan Nieder
2014-08-27  0:30                 ` [PATCH 04/20] refs.c: add transaction.status and track OPEN/CLOSED Jonathan Nieder
2014-08-27  0:30                 ` [PATCH 05/20] tag.c: use ref transactions when doing updates Jonathan Nieder
2014-08-27  0:31                 ` [PATCH 06/20] replace.c: use the ref transaction functions for updates Jonathan Nieder
2014-08-27  0:31                 ` [PATCH 07/20] commit.c: use ref transactions " Jonathan Nieder
2014-08-27  0:32                 ` [PATCH 08/20] sequencer.c: use ref transactions for all ref updates Jonathan Nieder
2014-08-27  0:32                 ` [PATCH 09/20] fast-import.c: change update_branch to use ref transactions Jonathan Nieder
2014-08-27  0:32                 ` [PATCH 10/20] branch.c: use ref transaction for all ref updates Jonathan Nieder
2014-08-27  0:33                 ` [PATCH 11/20] refs.c: change update_ref to use a transaction Jonathan Nieder
2014-08-27  0:33                 ` [PATCH 12/20] receive-pack.c: use a reference transaction for updating the refs Jonathan Nieder
2014-08-27  0:33                 ` [PATCH 13/20] fast-import.c: use a ref transaction when dumping tags Jonathan Nieder
2014-08-27  0:34                 ` [PATCH 14/20] walker.c: use ref transaction for ref updates Jonathan Nieder
2014-08-27  0:34                 ` [PATCH 15/20] refs.c: make lock_ref_sha1 static Jonathan Nieder
2014-08-27  0:35                 ` [PATCH 16/20] refs.c: remove the update_ref_lock function Jonathan Nieder
2014-08-27  0:35                 ` [PATCH 17/20] refs.c: remove the update_ref_write function Jonathan Nieder
2014-08-27  0:35                 ` [PATCH 18/20] refs.c: remove lock_ref_sha1 Jonathan Nieder
2014-08-27  0:36                 ` [PATCH 19/20] refs.c: make prune_ref use a transaction to delete the ref Jonathan Nieder
2014-08-27  0:36                 ` [PATCH 20/20] refs.c: make delete_ref use a transaction Jonathan Nieder
2014-08-27 21:29                 ` [PATCH 0/20] rs/ref-transaction-1 (Re: Transaction patch series overview) Junio C Hamano
2014-08-27 22:37                   ` Junio C Hamano
2014-09-02 20:58                 ` [PATCH v22 0/22] " Jonathan Nieder
2014-09-02 20:59                   ` [PATCH 01/22] refs.c: change ref_transaction_create to do error checking and return status Jonathan Nieder
2014-09-02 21:00                   ` [PATCH 02/22] refs.c: update ref_transaction_delete to check for error " Jonathan Nieder
2014-09-02 21:00                   ` [PATCH 03/22] refs.c: make ref_transaction_begin take an err argument Jonathan Nieder
2014-09-02 21:00                   ` [PATCH 04/22] refs.c: add transaction.status and track OPEN/CLOSED Jonathan Nieder
2014-09-02 21:01                   ` [PATCH 05/22] tag.c: use ref transactions when doing updates Jonathan Nieder
2014-09-02 21:01                   ` [PATCH 06/22] replace.c: use the ref transaction functions for updates Jonathan Nieder
2014-09-02 21:02                   ` [PATCH 07/22] commit.c: use ref transactions " Jonathan Nieder
2014-09-02 21:02                   ` [PATCH 08/22] sequencer.c: use ref transactions for all ref updates Jonathan Nieder
2014-09-02 21:03                   ` [PATCH 09/22] fast-import.c: change update_branch to use ref transactions Jonathan Nieder
2014-09-02 21:04                   ` [PATCH 10/22] branch.c: use ref transaction for all ref updates Jonathan Nieder
2014-09-02 21:04                   ` [PATCH 11/22] refs.c: change update_ref to use a transaction Jonathan Nieder
2014-09-02 21:05                   ` [PATCH 12/22] receive-pack.c: use a reference transaction for updating the refs Jonathan Nieder
2014-09-02 21:06                   ` [PATCH 13/22] fast-import.c: use a ref transaction when dumping tags Jonathan Nieder
2014-09-02 21:07                   ` [PATCH 14/22] walker.c: use ref transaction for ref updates Jonathan Nieder
2014-09-02 21:08                   ` [PATCH 15/22] refs.c: make lock_ref_sha1 static Jonathan Nieder
2014-09-02 21:08                   ` [PATCH 16/22] refs.c: remove the update_ref_lock function Jonathan Nieder
2014-09-02 21:08                   ` [PATCH 17/22] refs.c: remove the update_ref_write function Jonathan Nieder
2014-09-02 21:09                   ` [PATCH 18/22] refs.c: remove lock_ref_sha1 Jonathan Nieder
2014-09-02 21:09                   ` [PATCH 19/22] refs.c: make prune_ref use a transaction to delete the ref Jonathan Nieder
2014-09-02 21:10                   ` [PATCH 20/22] refs.c: make delete_ref use a transaction Jonathan Nieder
2014-09-02 21:10                   ` [PATCH 21/22] update-ref --stdin: narrow scope of err strbuf Jonathan Nieder
2014-09-02 21:11                   ` [PATCH 22/22] update-ref --stdin: pass transaction around explicitly Jonathan Nieder
2014-08-27 21:53           ` Using Gerrit to review Git patches (was: Re: Transaction patch series overview) Michael Haggerty
2014-08-28  5:07             ` Shawn Pearce
2014-09-11  3:03         ` [PATCH v21 0/19] rs/ref-transaction (Re: " Jonathan Nieder
2014-09-11  3:04           ` [PATCH 01/19] mv test: recreate mod/ directory instead of relying on stale copy Jonathan Nieder
2014-09-11  3:04           ` [PATCH 02/19] wrapper.c: remove/unlink_or_warn: simplify, treat ENOENT as success Jonathan Nieder
2014-09-11  3:05           ` [PATCH 03/19] wrapper.c: add a new function unlink_or_msg Jonathan Nieder
2014-09-11  3:06           ` [PATCH 04/19] refs.c: add an err argument to delete_ref_loose Jonathan Nieder
2014-09-11  3:06           ` [PATCH 05/19] refs.c: pass the ref log message to _create/delete/update instead of _commit Jonathan Nieder
2014-09-11  3:06           ` [PATCH 06/19] rename_ref: don't ask read_ref_full where the ref came from Jonathan Nieder
2014-09-11  3:07           ` [PATCH 07/19] refs.c: move the check for valid refname to lock_ref_sha1_basic Jonathan Nieder
2014-09-11  3:07           ` [PATCH 08/19] refs.c: call lock_ref_sha1_basic directly from commit Jonathan Nieder
2014-09-11  3:08           ` [PATCH 09/19] refs.c: pass a skip list to name_conflict_fn Jonathan Nieder
2014-09-11  3:08           ` [PATCH 10/19] refs.c: ref_transaction_commit: distinguish name conflicts from other errors Jonathan Nieder
2014-09-11  3:08           ` [PATCH 11/19] fetch.c: change s_update_ref to use a ref transaction Jonathan Nieder
2014-09-11  3:08           ` [PATCH 12/19] refs.c: make write_ref_sha1 static Jonathan Nieder
2014-09-11  3:09           ` [PATCH 13/19] refs.c: change resolve_ref_unsafe reading argument to be a flags field Jonathan Nieder
2014-09-11  3:10           ` [PATCH 14/19] branch -d: avoid repeated symref resolution Jonathan Nieder
2014-09-11  3:10           ` [PATCH 15/19] refs.c: fix handling of badly named refs Jonathan Nieder
2014-09-11  3:11           ` [PATCH 16/19] for-each-ref.c: improve message before aborting on broken ref Jonathan Nieder
2014-09-11  3:11           ` [PATCH 17/19] refs.c: do not permit err == NULL Jonathan Nieder
2014-09-11  3:12           ` [PATCH 18/19] lockfile: remove unable_to_lock_error Jonathan Nieder
2014-09-11  3:12           ` [PATCH 19/19] ref_transaction_commit: bail out on failure to remove a ref Jonathan Nieder
2014-09-11 21:40           ` [PATCH v21 0/19] rs/ref-transaction (Re: Transaction patch series overview) Junio C Hamano
2014-09-11 22:20           ` Junio C Hamano
2014-09-12  0:47             ` Jonathan Nieder
2014-09-12 19:00               ` Junio C Hamano
2014-09-12 19:18                 ` Jonathan Nieder
2014-09-12 19:56                   ` Junio C Hamano
2014-09-12 20:47                     ` Junio C Hamano
2014-09-13 17:52                       ` Junio C Hamano
2014-09-12 21:52                     ` Michael Haggerty
2014-09-12 23:57                       ` Jonathan Nieder
2014-09-17 13:23                         ` Michael Haggerty
2014-09-18 16:42                           ` Junio C Hamano
2014-09-18 16:57                             ` Jonathan Nieder
2014-09-18 17:26                               ` Junio C Hamano
2014-09-18 17:38                                 ` Jonathan Nieder
2014-09-25 21:35           ` Junio C Hamano
2014-09-25 21:40             ` Jonathan Nieder
2014-09-25 21:55               ` Junio C Hamano
2014-10-02  1:48           ` [PATCH v22 0/24] rs/ref-transaction Jonathan Nieder
2014-10-02  1:50             ` [PATCH 01/24] mv test: recreate mod/ directory instead of relying on stale copy Jonathan Nieder
2014-10-02  1:54             ` [PATCH 02/24] wrapper.c: remove/unlink_or_warn: simplify, treat ENOENT as success Jonathan Nieder
2014-10-02  1:55             ` [PATCH 03/24] wrapper.c: add a new function unlink_or_msg Jonathan Nieder
2014-10-02  1:58             ` [PATCH 04/24] refs.c: add an err argument to delete_ref_loose Jonathan Nieder
2014-10-02  1:59             ` [PATCH 05/24] refs.c: pass the ref log message to _create/delete/update instead of _commit Jonathan Nieder
2014-10-02  2:00             ` [PATCH 06/24] rename_ref: don't ask read_ref_full where the ref came from Jonathan Nieder
2014-10-02  2:01             ` [PATCH 07/24] refs.c: refuse to lock badly named refs in lock_ref_sha1_basic Jonathan Nieder
2014-10-02  2:02             ` [PATCH 08/24] refs.c: call lock_ref_sha1_basic directly from commit Jonathan Nieder
2014-10-02  2:03             ` [PATCH 09/24] refs.c: pass a list of names to skip to is_refname_available Jonathan Nieder
2014-10-02 19:18               ` Junio C Hamano
2014-10-03 18:51                 ` Jonathan Nieder
2014-10-03 19:05                   ` Junio C Hamano
2014-10-03 21:39                     ` [PATCH v22.5 " Jonathan Nieder
2014-10-07 19:26                       ` Junio C Hamano
2014-10-02  2:05             ` [PATCH 10/24] refs.c: ref_transaction_commit: distinguish name conflicts from other errors Jonathan Nieder
2014-10-02  2:07             ` [PATCH 11/24] fetch.c: change s_update_ref to use a ref transaction Jonathan Nieder
2014-10-02  2:08             ` [PATCH 12/24] refs.c: make write_ref_sha1 static Jonathan Nieder
2014-10-02  2:10             ` [PATCH 13/24] refs.c: change resolve_ref_unsafe reading argument to be a flags field Jonathan Nieder
2014-10-02  2:10             ` [PATCH 14/24] reflog test: test interaction with detached HEAD Jonathan Nieder
2014-10-02  2:15             ` [PATCH 15/24] branch -d: avoid repeated symref resolution Jonathan Nieder
2014-10-02  2:15             ` [PATCH 16/24] branch -d: simplify by using RESOLVE_REF_READING flag Jonathan Nieder
2014-10-02  2:16             ` [PATCH 17/24] packed-ref cache: forbid dot-components in refnames Jonathan Nieder
2014-10-02  2:17             ` [PATCH 18/24] test: put tests for handling of bad ref names in one place Jonathan Nieder
2014-10-02  2:28             ` [PATCH 19/24] refs.c: allow listing and deleting badly named refs Jonathan Nieder
2014-10-02 18:55               ` Junio C Hamano
2014-10-03 20:25                 ` Ronnie Sahlberg
2014-10-03 20:32                   ` Ronnie Sahlberg [this message]
2014-10-03 20:39                   ` Junio C Hamano
2014-10-02  2:30             ` [PATCH 20/24] for-each-ref.c: improve message before aborting on broken ref Jonathan Nieder
2014-10-02  2:32             ` [PATCH 21/24] remote rm/prune: print a message when writing packed-refs fails Jonathan Nieder
2014-10-02  2:33             ` [PATCH 22/24] refs.c: do not permit err == NULL Jonathan Nieder
2014-10-02  2:34             ` [PATCH 23/24] lockfile: remove unable_to_lock_error Jonathan Nieder
2014-10-02  2:35             ` [PATCH 24/24] ref_transaction_commit: bail out on failure to remove a ref Jonathan Nieder

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: http://vger.kernel.org/majordomo-info.html

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CAL=YDWmV89jwWqru5dgxCKpx-JYCrH=vYVLT_yPqsT9Ep9Qa8Q@mail.gmail.com' \
    --to=sahlberg@google.com \
    --cc=git@vger.kernel.org \
    --cc=gitster@pobox.com \
    --cc=jrnieder@gmail.com \
    --cc=mhagger@alum.mit.edu \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

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 read-only IMAP folder(s) and NNTP newsgroup(s).