2015-11-09 13:34:01 +00:00
|
|
|
/*
|
|
|
|
* The backend-independent part of the reference module.
|
|
|
|
*/
|
|
|
|
|
2005-06-06 20:31:29 +00:00
|
|
|
#include "cache.h"
|
2017-06-14 18:07:36 +00:00
|
|
|
#include "config.h"
|
2017-02-10 11:16:15 +00:00
|
|
|
#include "hashmap.h"
|
2014-10-01 10:28:42 +00:00
|
|
|
#include "lockfile.h"
|
2017-04-16 06:41:26 +00:00
|
|
|
#include "iterator.h"
|
2006-12-19 22:34:12 +00:00
|
|
|
#include "refs.h"
|
2015-11-10 11:42:36 +00:00
|
|
|
#include "refs/refs-internal.h"
|
2018-05-15 23:42:15 +00:00
|
|
|
#include "object-store.h"
|
2006-11-19 21:22:44 +00:00
|
|
|
#include "object.h"
|
|
|
|
#include "tag.h"
|
2017-03-26 02:42:31 +00:00
|
|
|
#include "submodule.h"
|
2017-04-24 10:01:22 +00:00
|
|
|
#include "worktree.h"
|
2018-03-15 17:31:24 +00:00
|
|
|
#include "argv-array.h"
|
2018-04-12 00:21:09 +00:00
|
|
|
#include "repository.h"
|
2014-12-12 08:57:02 +00:00
|
|
|
|
2016-09-04 16:08:10 +00:00
|
|
|
/*
|
|
|
|
* List of all available backends
|
|
|
|
*/
|
|
|
|
static struct ref_storage_be *refs_backends = &refs_be_files;
|
|
|
|
|
|
|
|
static struct ref_storage_be *find_ref_storage_backend(const char *name)
|
|
|
|
{
|
|
|
|
struct ref_storage_be *be;
|
|
|
|
for (be = refs_backends; be; be = be->next)
|
|
|
|
if (!strcmp(be->name, name))
|
|
|
|
return be;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ref_storage_backend_exists(const char *name)
|
|
|
|
{
|
|
|
|
return find_ref_storage_backend(name) != NULL;
|
|
|
|
}
|
|
|
|
|
2012-04-10 05:30:13 +00:00
|
|
|
/*
|
2014-06-04 03:38:10 +00:00
|
|
|
* How to handle various characters in refnames:
|
|
|
|
* 0: An acceptable character for refs
|
2014-07-28 17:41:53 +00:00
|
|
|
* 1: End-of-component
|
|
|
|
* 2: ., look for a preceding . to reject .. in refs
|
|
|
|
* 3: {, look for a preceding @ to reject @{ in refs
|
2015-07-22 21:05:32 +00:00
|
|
|
* 4: A bad character: ASCII control characters, and
|
2015-07-22 21:05:33 +00:00
|
|
|
* ":", "?", "[", "\", "^", "~", SP, or TAB
|
|
|
|
* 5: *, reject unless REFNAME_REFSPEC_PATTERN is set
|
2014-06-04 03:38:10 +00:00
|
|
|
*/
|
|
|
|
static unsigned char refname_disposition[256] = {
|
2014-07-28 17:41:53 +00:00
|
|
|
1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
|
|
|
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
2015-07-22 21:05:33 +00:00
|
|
|
4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 2, 1,
|
2014-07-28 17:41:53 +00:00
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 4,
|
2014-06-04 03:38:10 +00:00
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
2014-07-28 17:41:53 +00:00
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 4, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 4, 4
|
2014-06-04 03:38:10 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Try to read one refname component from the front of refname.
|
|
|
|
* Return the length of the component found, or -1 if the component is
|
|
|
|
* not legal. It is legal if it is something reasonable to have under
|
|
|
|
* ".git/refs/"; We do not like it if:
|
2012-04-10 05:30:13 +00:00
|
|
|
*
|
|
|
|
* - any path component of it begins with ".", or
|
|
|
|
* - it has double dots "..", or
|
2015-07-22 21:05:32 +00:00
|
|
|
* - it has ASCII control characters, or
|
2015-07-22 21:05:33 +00:00
|
|
|
* - it has ":", "?", "[", "\", "^", "~", SP, or TAB anywhere, or
|
|
|
|
* - it has "*" anywhere unless REFNAME_REFSPEC_PATTERN is set, or
|
2015-07-22 21:05:32 +00:00
|
|
|
* - it ends with a "/", or
|
|
|
|
* - it ends with ".lock", or
|
|
|
|
* - it contains a "@{" portion
|
2012-04-10 05:30:13 +00:00
|
|
|
*/
|
2015-07-22 21:05:33 +00:00
|
|
|
static int check_refname_component(const char *refname, int *flags)
|
2012-04-10 05:30:13 +00:00
|
|
|
{
|
|
|
|
const char *cp;
|
|
|
|
char last = '\0';
|
|
|
|
|
|
|
|
for (cp = refname; ; cp++) {
|
2014-06-04 03:38:10 +00:00
|
|
|
int ch = *cp & 255;
|
|
|
|
unsigned char disp = refname_disposition[ch];
|
|
|
|
switch (disp) {
|
2014-07-28 17:41:53 +00:00
|
|
|
case 1:
|
2014-06-04 03:38:10 +00:00
|
|
|
goto out;
|
2014-07-28 17:41:53 +00:00
|
|
|
case 2:
|
2014-06-04 03:38:10 +00:00
|
|
|
if (last == '.')
|
|
|
|
return -1; /* Refname contains "..". */
|
|
|
|
break;
|
2014-07-28 17:41:53 +00:00
|
|
|
case 3:
|
2014-06-04 03:38:10 +00:00
|
|
|
if (last == '@')
|
|
|
|
return -1; /* Refname contains "@{". */
|
2012-04-10 05:30:13 +00:00
|
|
|
break;
|
2014-07-28 17:41:53 +00:00
|
|
|
case 4:
|
2014-06-04 03:38:10 +00:00
|
|
|
return -1;
|
2015-07-22 21:05:33 +00:00
|
|
|
case 5:
|
|
|
|
if (!(*flags & REFNAME_REFSPEC_PATTERN))
|
|
|
|
return -1; /* refspec can't be a pattern */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Unset the pattern flag so that we only accept
|
|
|
|
* a single asterisk for one side of refspec.
|
|
|
|
*/
|
|
|
|
*flags &= ~ REFNAME_REFSPEC_PATTERN;
|
|
|
|
break;
|
2014-06-04 03:38:10 +00:00
|
|
|
}
|
2012-04-10 05:30:13 +00:00
|
|
|
last = ch;
|
|
|
|
}
|
2014-06-04 03:38:10 +00:00
|
|
|
out:
|
2012-04-10 05:30:13 +00:00
|
|
|
if (cp == refname)
|
2012-04-10 05:30:22 +00:00
|
|
|
return 0; /* Component has zero length. */
|
2014-09-26 19:22:22 +00:00
|
|
|
if (refname[0] == '.')
|
|
|
|
return -1; /* Component starts with '.'. */
|
2014-10-01 10:28:15 +00:00
|
|
|
if (cp - refname >= LOCK_SUFFIX_LEN &&
|
|
|
|
!memcmp(cp - LOCK_SUFFIX_LEN, LOCK_SUFFIX, LOCK_SUFFIX_LEN))
|
2012-04-10 05:30:13 +00:00
|
|
|
return -1; /* Refname ends with ".lock". */
|
|
|
|
return cp - refname;
|
|
|
|
}
|
|
|
|
|
2014-07-28 17:41:53 +00:00
|
|
|
int check_refname_format(const char *refname, int flags)
|
2012-04-10 05:30:13 +00:00
|
|
|
{
|
|
|
|
int component_len, component_count = 0;
|
|
|
|
|
Add new @ shortcut for HEAD
Typing 'HEAD' is tedious, especially when we can use '@' instead.
The reason for choosing '@' is that it follows naturally from the
ref@op syntax (e.g. HEAD@{u}), except we have no ref, and no
operation, and when we don't have those, it makes sens to assume
'HEAD'.
So now we can use 'git show @~1', and all that goody goodness.
Until now '@' was a valid name, but it conflicts with this idea, so
let's make it invalid. Probably very few people, if any, used this name.
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-09-02 06:34:30 +00:00
|
|
|
if (!strcmp(refname, "@"))
|
|
|
|
/* Refname is a single character '@'. */
|
|
|
|
return -1;
|
|
|
|
|
2012-04-10 05:30:13 +00:00
|
|
|
while (1) {
|
|
|
|
/* We are at the start of a path component. */
|
2015-07-22 21:05:33 +00:00
|
|
|
component_len = check_refname_component(refname, &flags);
|
|
|
|
if (component_len <= 0)
|
|
|
|
return -1;
|
|
|
|
|
2012-04-10 05:30:13 +00:00
|
|
|
component_count++;
|
|
|
|
if (refname[component_len] == '\0')
|
|
|
|
break;
|
|
|
|
/* Skip to next component. */
|
|
|
|
refname += component_len + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (refname[component_len - 1] == '.')
|
|
|
|
return -1; /* Refname ends with '.'. */
|
|
|
|
if (!(flags & REFNAME_ALLOW_ONELEVEL) && component_count < 2)
|
|
|
|
return -1; /* Refname has only one component. */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-11-10 11:42:36 +00:00
|
|
|
int refname_is_safe(const char *refname)
|
refs.c: allow listing and deleting badly named refs
We currently do not handle badly named refs well:
$ cp .git/refs/heads/master .git/refs/heads/master.....@\*@\\.
$ git branch
fatal: Reference has invalid format: 'refs/heads/master.....@*@\.'
$ git branch -D master.....@\*@\\.
error: branch 'master.....@*@\.' not found.
Users cannot recover from a badly named ref without manually finding
and deleting the loose ref file or appropriate line in packed-refs.
Making that easier will make it easier to tweak the ref naming rules
in the future, for example to forbid shell metacharacters like '`'
and '"', without putting people in a state that is hard to get out of.
So allow "branch --list" to show these refs and allow "branch -d/-D"
and "update-ref -d" to delete them. Other commands (for example to
rename refs) will continue to not handle these refs but can be changed
in later patches.
Details:
In resolving functions, refuse to resolve refs that don't pass the
git-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 (but not symrefs pointing to badly named refs)
with a REF_BAD_NAME flag to make it easier for future callers to
notice and handle them specially. For example, in a later patch
for-each-ref will use this flag to detect refs whose names can confuse
callers parsing for-each-ref output.
In the transaction API, refuse to create or update badly named refs,
but allow deleting them (unless they try to escape refs/ and don't match
[A-Z_]*).
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-09-03 18:45:43 +00:00
|
|
|
{
|
2016-04-27 10:39:11 +00:00
|
|
|
const char *rest;
|
|
|
|
|
|
|
|
if (skip_prefix(refname, "refs/", &rest)) {
|
refs.c: allow listing and deleting badly named refs
We currently do not handle badly named refs well:
$ cp .git/refs/heads/master .git/refs/heads/master.....@\*@\\.
$ git branch
fatal: Reference has invalid format: 'refs/heads/master.....@*@\.'
$ git branch -D master.....@\*@\\.
error: branch 'master.....@*@\.' not found.
Users cannot recover from a badly named ref without manually finding
and deleting the loose ref file or appropriate line in packed-refs.
Making that easier will make it easier to tweak the ref naming rules
in the future, for example to forbid shell metacharacters like '`'
and '"', without putting people in a state that is hard to get out of.
So allow "branch --list" to show these refs and allow "branch -d/-D"
and "update-ref -d" to delete them. Other commands (for example to
rename refs) will continue to not handle these refs but can be changed
in later patches.
Details:
In resolving functions, refuse to resolve refs that don't pass the
git-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 (but not symrefs pointing to badly named refs)
with a REF_BAD_NAME flag to make it easier for future callers to
notice and handle them specially. For example, in a later patch
for-each-ref will use this flag to detect refs whose names can confuse
callers parsing for-each-ref output.
In the transaction API, refuse to create or update badly named refs,
but allow deleting them (unless they try to escape refs/ and don't match
[A-Z_]*).
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-09-03 18:45:43 +00:00
|
|
|
char *buf;
|
|
|
|
int result;
|
2016-04-27 10:40:39 +00:00
|
|
|
size_t restlen = strlen(rest);
|
|
|
|
|
|
|
|
/* rest must not be empty, or start or end with "/" */
|
|
|
|
if (!restlen || *rest == '/' || rest[restlen - 1] == '/')
|
|
|
|
return 0;
|
refs.c: allow listing and deleting badly named refs
We currently do not handle badly named refs well:
$ cp .git/refs/heads/master .git/refs/heads/master.....@\*@\\.
$ git branch
fatal: Reference has invalid format: 'refs/heads/master.....@*@\.'
$ git branch -D master.....@\*@\\.
error: branch 'master.....@*@\.' not found.
Users cannot recover from a badly named ref without manually finding
and deleting the loose ref file or appropriate line in packed-refs.
Making that easier will make it easier to tweak the ref naming rules
in the future, for example to forbid shell metacharacters like '`'
and '"', without putting people in a state that is hard to get out of.
So allow "branch --list" to show these refs and allow "branch -d/-D"
and "update-ref -d" to delete them. Other commands (for example to
rename refs) will continue to not handle these refs but can be changed
in later patches.
Details:
In resolving functions, refuse to resolve refs that don't pass the
git-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 (but not symrefs pointing to badly named refs)
with a REF_BAD_NAME flag to make it easier for future callers to
notice and handle them specially. For example, in a later patch
for-each-ref will use this flag to detect refs whose names can confuse
callers parsing for-each-ref output.
In the transaction API, refuse to create or update badly named refs,
but allow deleting them (unless they try to escape refs/ and don't match
[A-Z_]*).
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-09-03 18:45:43 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Does the refname try to escape refs/?
|
|
|
|
* For example: refs/foo/../bar is safe but refs/foo/../../bar
|
|
|
|
* is not.
|
|
|
|
*/
|
2016-04-27 10:40:39 +00:00
|
|
|
buf = xmallocz(restlen);
|
|
|
|
result = !normalize_path_copy(buf, rest) && !strcmp(buf, rest);
|
refs.c: allow listing and deleting badly named refs
We currently do not handle badly named refs well:
$ cp .git/refs/heads/master .git/refs/heads/master.....@\*@\\.
$ git branch
fatal: Reference has invalid format: 'refs/heads/master.....@*@\.'
$ git branch -D master.....@\*@\\.
error: branch 'master.....@*@\.' not found.
Users cannot recover from a badly named ref without manually finding
and deleting the loose ref file or appropriate line in packed-refs.
Making that easier will make it easier to tweak the ref naming rules
in the future, for example to forbid shell metacharacters like '`'
and '"', without putting people in a state that is hard to get out of.
So allow "branch --list" to show these refs and allow "branch -d/-D"
and "update-ref -d" to delete them. Other commands (for example to
rename refs) will continue to not handle these refs but can be changed
in later patches.
Details:
In resolving functions, refuse to resolve refs that don't pass the
git-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 (but not symrefs pointing to badly named refs)
with a REF_BAD_NAME flag to make it easier for future callers to
notice and handle them specially. For example, in a later patch
for-each-ref will use this flag to detect refs whose names can confuse
callers parsing for-each-ref output.
In the transaction API, refuse to create or update badly named refs,
but allow deleting them (unless they try to escape refs/ and don't match
[A-Z_]*).
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-09-03 18:45:43 +00:00
|
|
|
free(buf);
|
|
|
|
return result;
|
|
|
|
}
|
2016-04-27 10:42:27 +00:00
|
|
|
|
|
|
|
do {
|
refs.c: allow listing and deleting badly named refs
We currently do not handle badly named refs well:
$ cp .git/refs/heads/master .git/refs/heads/master.....@\*@\\.
$ git branch
fatal: Reference has invalid format: 'refs/heads/master.....@*@\.'
$ git branch -D master.....@\*@\\.
error: branch 'master.....@*@\.' not found.
Users cannot recover from a badly named ref without manually finding
and deleting the loose ref file or appropriate line in packed-refs.
Making that easier will make it easier to tweak the ref naming rules
in the future, for example to forbid shell metacharacters like '`'
and '"', without putting people in a state that is hard to get out of.
So allow "branch --list" to show these refs and allow "branch -d/-D"
and "update-ref -d" to delete them. Other commands (for example to
rename refs) will continue to not handle these refs but can be changed
in later patches.
Details:
In resolving functions, refuse to resolve refs that don't pass the
git-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 (but not symrefs pointing to badly named refs)
with a REF_BAD_NAME flag to make it easier for future callers to
notice and handle them specially. For example, in a later patch
for-each-ref will use this flag to detect refs whose names can confuse
callers parsing for-each-ref output.
In the transaction API, refuse to create or update badly named refs,
but allow deleting them (unless they try to escape refs/ and don't match
[A-Z_]*).
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-09-03 18:45:43 +00:00
|
|
|
if (!isupper(*refname) && *refname != '_')
|
|
|
|
return 0;
|
|
|
|
refname++;
|
2016-04-27 10:42:27 +00:00
|
|
|
} while (*refname);
|
refs.c: allow listing and deleting badly named refs
We currently do not handle badly named refs well:
$ cp .git/refs/heads/master .git/refs/heads/master.....@\*@\\.
$ git branch
fatal: Reference has invalid format: 'refs/heads/master.....@*@\.'
$ git branch -D master.....@\*@\\.
error: branch 'master.....@*@\.' not found.
Users cannot recover from a badly named ref without manually finding
and deleting the loose ref file or appropriate line in packed-refs.
Making that easier will make it easier to tweak the ref naming rules
in the future, for example to forbid shell metacharacters like '`'
and '"', without putting people in a state that is hard to get out of.
So allow "branch --list" to show these refs and allow "branch -d/-D"
and "update-ref -d" to delete them. Other commands (for example to
rename refs) will continue to not handle these refs but can be changed
in later patches.
Details:
In resolving functions, refuse to resolve refs that don't pass the
git-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 (but not symrefs pointing to badly named refs)
with a REF_BAD_NAME flag to make it easier for future callers to
notice and handle them specially. For example, in a later patch
for-each-ref will use this flag to detect refs whose names can confuse
callers parsing for-each-ref output.
In the transaction API, refuse to create or update badly named refs,
but allow deleting them (unless they try to escape refs/ and don't match
[A-Z_]*).
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-09-03 18:45:43 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2017-06-23 07:01:37 +00:00
|
|
|
/*
|
|
|
|
* Return true if refname, which has the specified oid and flags, can
|
|
|
|
* be resolved to an object in the database. If the referred-to object
|
|
|
|
* does not exist, emit a warning and return false.
|
|
|
|
*/
|
|
|
|
int ref_resolves_to_object(const char *refname,
|
|
|
|
const struct object_id *oid,
|
|
|
|
unsigned int flags)
|
|
|
|
{
|
|
|
|
if (flags & REF_ISBROKEN)
|
|
|
|
return 0;
|
|
|
|
if (!has_sha1_file(oid->hash)) {
|
2018-07-21 07:49:35 +00:00
|
|
|
error(_("%s does not point to a valid object!"), refname);
|
2017-06-23 07:01:37 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2017-03-26 02:42:34 +00:00
|
|
|
char *refs_resolve_refdup(struct ref_store *refs,
|
|
|
|
const char *refname, int resolve_flags,
|
refs: convert resolve_refdup and refs_resolve_refdup to struct object_id
All of the callers already pass the hash member of struct object_id, so
update them to pass a pointer to the struct directly,
This transformation was done with an update to declaration and
definition and the following semantic patch:
@@
expression E1, E2, E3, E4;
@@
- resolve_refdup(E1, E2, E3.hash, E4)
+ resolve_refdup(E1, E2, &E3, E4)
@@
expression E1, E2, E3, E4;
@@
- resolve_refdup(E1, E2, E3->hash, E4)
+ resolve_refdup(E1, E2, E3, E4)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-10-15 22:06:55 +00:00
|
|
|
struct object_id *oid, int *flags)
|
2017-03-26 02:42:34 +00:00
|
|
|
{
|
|
|
|
const char *result;
|
|
|
|
|
|
|
|
result = refs_resolve_ref_unsafe(refs, refname, resolve_flags,
|
refs: convert resolve_ref_unsafe to struct object_id
Convert resolve_ref_unsafe to take a pointer to struct object_id by
converting one remaining caller to use struct object_id, removing the
temporary NULL pointer check in expand_ref, converting the declaration
and definition, and applying the following semantic patch:
@@
expression E1, E2, E3, E4;
@@
- resolve_ref_unsafe(E1, E2, E3.hash, E4)
+ resolve_ref_unsafe(E1, E2, &E3, E4)
@@
expression E1, E2, E3, E4;
@@
- resolve_ref_unsafe(E1, E2, E3->hash, E4)
+ resolve_ref_unsafe(E1, E2, E3, E4)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-10-15 22:07:09 +00:00
|
|
|
oid, flags);
|
2017-03-26 02:42:34 +00:00
|
|
|
return xstrdup_or_null(result);
|
|
|
|
}
|
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
char *resolve_refdup(const char *refname, int resolve_flags,
|
refs: convert resolve_refdup and refs_resolve_refdup to struct object_id
All of the callers already pass the hash member of struct object_id, so
update them to pass a pointer to the struct directly,
This transformation was done with an update to declaration and
definition and the following semantic patch:
@@
expression E1, E2, E3, E4;
@@
- resolve_refdup(E1, E2, E3.hash, E4)
+ resolve_refdup(E1, E2, &E3, E4)
@@
expression E1, E2, E3, E4;
@@
- resolve_refdup(E1, E2, E3->hash, E4)
+ resolve_refdup(E1, E2, E3, E4)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-10-15 22:06:55 +00:00
|
|
|
struct object_id *oid, int *flags)
|
Start handling references internally as a sorted in-memory list
This also adds some very rudimentary support for the notion of packed
refs. HOWEVER! At this point it isn't used to actually look up a ref
yet, only for listing them (ie "for_each_ref()" and friends see the
packed refs, but none of the other single-ref lookup routines).
Note how we keep two separate lists: one for the loose refs, and one for
the packed refs we read. That's so that we can easily keep the two apart,
and read only one set or the other (and still always make sure that the
loose refs take precedence).
[ From this, it's not actually obvious why we'd keep the two separate
lists, but it's important to have the packed refs on their own list
later on, when I add support for looking up a single loose one.
For that case, we will want to read _just_ the packed refs in case the
single-ref lookup fails, yet we may end up needing the other list at
some point in the future, so keeping them separated is important ]
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2006-09-11 23:37:32 +00:00
|
|
|
{
|
2018-04-12 00:21:09 +00:00
|
|
|
return refs_resolve_refdup(get_main_ref_store(the_repository),
|
2017-03-26 02:42:34 +00:00
|
|
|
refname, resolve_flags,
|
refs: convert resolve_refdup and refs_resolve_refdup to struct object_id
All of the callers already pass the hash member of struct object_id, so
update them to pass a pointer to the struct directly,
This transformation was done with an update to declaration and
definition and the following semantic patch:
@@
expression E1, E2, E3, E4;
@@
- resolve_refdup(E1, E2, E3.hash, E4)
+ resolve_refdup(E1, E2, &E3, E4)
@@
expression E1, E2, E3, E4;
@@
- resolve_refdup(E1, E2, E3->hash, E4)
+ resolve_refdup(E1, E2, E3, E4)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-10-15 22:06:55 +00:00
|
|
|
oid, flags);
|
2011-12-12 05:38:22 +00:00
|
|
|
}
|
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
/* The argument to filter_refs */
|
|
|
|
struct ref_filter {
|
|
|
|
const char *pattern;
|
|
|
|
each_ref_fn *fn;
|
|
|
|
void *cb_data;
|
|
|
|
};
|
2012-04-10 05:30:26 +00:00
|
|
|
|
2017-03-26 02:42:34 +00:00
|
|
|
int refs_read_ref_full(struct ref_store *refs, const char *refname,
|
2017-10-15 22:06:56 +00:00
|
|
|
int resolve_flags, struct object_id *oid, int *flags)
|
2012-04-10 05:30:21 +00:00
|
|
|
{
|
refs: convert resolve_ref_unsafe to struct object_id
Convert resolve_ref_unsafe to take a pointer to struct object_id by
converting one remaining caller to use struct object_id, removing the
temporary NULL pointer check in expand_ref, converting the declaration
and definition, and applying the following semantic patch:
@@
expression E1, E2, E3, E4;
@@
- resolve_ref_unsafe(E1, E2, E3.hash, E4)
+ resolve_ref_unsafe(E1, E2, &E3, E4)
@@
expression E1, E2, E3, E4;
@@
- resolve_ref_unsafe(E1, E2, E3->hash, E4)
+ resolve_ref_unsafe(E1, E2, E3, E4)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-10-15 22:07:09 +00:00
|
|
|
if (refs_resolve_ref_unsafe(refs, refname, resolve_flags, oid, flags))
|
2015-11-09 13:34:01 +00:00
|
|
|
return 0;
|
|
|
|
return -1;
|
2012-04-10 05:30:21 +00:00
|
|
|
}
|
|
|
|
|
2017-10-15 22:06:56 +00:00
|
|
|
int read_ref_full(const char *refname, int resolve_flags, struct object_id *oid, int *flags)
|
2017-03-26 02:42:34 +00:00
|
|
|
{
|
2018-04-12 00:21:09 +00:00
|
|
|
return refs_read_ref_full(get_main_ref_store(the_repository), refname,
|
2017-10-15 22:06:56 +00:00
|
|
|
resolve_flags, oid, flags);
|
2017-03-26 02:42:34 +00:00
|
|
|
}
|
|
|
|
|
2017-10-15 22:06:56 +00:00
|
|
|
int read_ref(const char *refname, struct object_id *oid)
|
2011-12-12 05:38:22 +00:00
|
|
|
{
|
2017-10-15 22:06:56 +00:00
|
|
|
return read_ref_full(refname, RESOLVE_REF_READING, oid, NULL);
|
2007-04-17 01:42:50 +00:00
|
|
|
}
|
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
int ref_exists(const char *refname)
|
2012-04-10 05:30:13 +00:00
|
|
|
{
|
2017-09-23 09:45:04 +00:00
|
|
|
return !!resolve_ref_unsafe(refname, RESOLVE_REF_READING, NULL, NULL);
|
2012-04-10 05:30:13 +00:00
|
|
|
}
|
|
|
|
|
log: add option to choose which refs to decorate
When `log --decorate` is used, git will decorate commits with all
available refs. While in most cases this may give the desired effect,
under some conditions it can lead to excessively verbose output.
Introduce two command line options, `--decorate-refs=<pattern>` and
`--decorate-refs-exclude=<pattern>` to allow the user to select which
refs are used in decoration.
When "--decorate-refs=<pattern>" is given, only the refs that match the
pattern are used in decoration. The refs that match the pattern when
"--decorate-refs-exclude=<pattern>" is given, are never used in
decoration.
These options follow the same convention for mixing negative and
positive patterns across the system, assuming that the inclusive default
is to match all refs available.
(1) if there is no positive pattern given, pretend as if an
inclusive default positive pattern was given;
(2) for each candidate, reject it if it matches no positive
pattern, or if it matches any one of the negative patterns.
The rules for what is considered a match are slightly different from the
rules used elsewhere.
Commands like `log --glob` assume a trailing '/*' when glob chars are
not present in the pattern. This makes it difficult to specify a single
ref. On the other hand, commands like `describe --match --all` allow
specifying exact refs, but do not have the convenience of allowing
"shorthand refs" like 'refs/heads' or 'heads' to refer to
'refs/heads/*'.
The commands introduced in this patch consider a match if:
(a) the pattern contains globs chars,
and regular pattern matching returns a match.
(b) the pattern does not contain glob chars,
and ref '<pattern>' exists, or if ref exists under '<pattern>/'
This allows both behaviours (allowing single refs and shorthand refs)
yet remaining compatible with existent commands.
Helped-by: Kevin Daudt <me@ikke.info>
Helped-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Rafael Ascensão <rafa.almas@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-11-21 21:33:41 +00:00
|
|
|
static int match_ref_pattern(const char *refname,
|
|
|
|
const struct string_list_item *item)
|
|
|
|
{
|
|
|
|
int matched = 0;
|
|
|
|
if (item->util == NULL) {
|
|
|
|
if (!wildmatch(item->string, refname, 0))
|
|
|
|
matched = 1;
|
|
|
|
} else {
|
|
|
|
const char *rest;
|
|
|
|
if (skip_prefix(refname, item->string, &rest) &&
|
|
|
|
(!*rest || *rest == '/'))
|
|
|
|
matched = 1;
|
|
|
|
}
|
|
|
|
return matched;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ref_filter_match(const char *refname,
|
|
|
|
const struct string_list *include_patterns,
|
|
|
|
const struct string_list *exclude_patterns)
|
|
|
|
{
|
|
|
|
struct string_list_item *item;
|
|
|
|
|
|
|
|
if (exclude_patterns && exclude_patterns->nr) {
|
|
|
|
for_each_string_list_item(item, exclude_patterns) {
|
|
|
|
if (match_ref_pattern(refname, item))
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (include_patterns && include_patterns->nr) {
|
|
|
|
int found = 0;
|
|
|
|
for_each_string_list_item(item, include_patterns) {
|
|
|
|
if (match_ref_pattern(refname, item)) {
|
|
|
|
found = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
static int filter_refs(const char *refname, const struct object_id *oid,
|
|
|
|
int flags, void *data)
|
2012-04-10 05:30:26 +00:00
|
|
|
{
|
2015-11-09 13:34:01 +00:00
|
|
|
struct ref_filter *filter = (struct ref_filter *)data;
|
|
|
|
|
2017-06-22 21:38:08 +00:00
|
|
|
if (wildmatch(filter->pattern, refname, 0))
|
2015-11-09 13:34:01 +00:00
|
|
|
return 0;
|
|
|
|
return filter->fn(refname, oid, flags, filter->cb_data);
|
2012-04-10 05:30:26 +00:00
|
|
|
}
|
|
|
|
|
2017-10-15 22:07:10 +00:00
|
|
|
enum peel_status peel_object(const struct object_id *name, struct object_id *oid)
|
2007-04-17 01:42:50 +00:00
|
|
|
{
|
2017-10-15 22:07:10 +00:00
|
|
|
struct object *o = lookup_unknown_object(name->hash);
|
2007-04-17 01:42:50 +00:00
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
if (o->type == OBJ_NONE) {
|
2018-04-25 18:20:59 +00:00
|
|
|
int type = oid_object_info(the_repository, name, NULL);
|
2018-06-29 01:21:54 +00:00
|
|
|
if (type < 0 || !object_as_type(the_repository, o, type, 0))
|
2015-11-09 13:34:01 +00:00
|
|
|
return PEEL_INVALID;
|
|
|
|
}
|
2012-04-10 05:30:13 +00:00
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
if (o->type != OBJ_TAG)
|
|
|
|
return PEEL_NON_TAG;
|
2012-05-22 21:03:29 +00:00
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
o = deref_tag_noverify(o);
|
|
|
|
if (!o)
|
|
|
|
return PEEL_INVALID;
|
|
|
|
|
2017-10-15 22:07:10 +00:00
|
|
|
oidcpy(oid, &o->oid);
|
2015-11-09 13:34:01 +00:00
|
|
|
return PEEL_PEELED;
|
2012-05-22 21:03:29 +00:00
|
|
|
}
|
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
struct warn_if_dangling_data {
|
|
|
|
FILE *fp;
|
|
|
|
const char *refname;
|
|
|
|
const struct string_list *refnames;
|
|
|
|
const char *msg_fmt;
|
|
|
|
};
|
2012-04-10 05:30:13 +00:00
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
static int warn_if_dangling_symref(const char *refname, const struct object_id *oid,
|
|
|
|
int flags, void *cb_data)
|
|
|
|
{
|
|
|
|
struct warn_if_dangling_data *d = cb_data;
|
|
|
|
const char *resolves_to;
|
2012-04-10 05:30:13 +00:00
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
if (!(flags & REF_ISSYMREF))
|
|
|
|
return 0;
|
2012-04-10 05:30:13 +00:00
|
|
|
|
2017-09-23 09:45:04 +00:00
|
|
|
resolves_to = resolve_ref_unsafe(refname, 0, NULL, NULL);
|
2015-11-09 13:34:01 +00:00
|
|
|
if (!resolves_to
|
|
|
|
|| (d->refname
|
|
|
|
? strcmp(resolves_to, d->refname)
|
|
|
|
: !string_list_has_string(d->refnames, resolves_to))) {
|
|
|
|
return 0;
|
|
|
|
}
|
2012-04-10 05:30:13 +00:00
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
fprintf(d->fp, d->msg_fmt, refname);
|
|
|
|
fputc('\n', d->fp);
|
|
|
|
return 0;
|
2012-04-10 05:30:13 +00:00
|
|
|
}
|
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname)
|
2012-04-24 22:45:11 +00:00
|
|
|
{
|
2015-11-09 13:34:01 +00:00
|
|
|
struct warn_if_dangling_data data;
|
|
|
|
|
|
|
|
data.fp = fp;
|
|
|
|
data.refname = refname;
|
|
|
|
data.refnames = NULL;
|
|
|
|
data.msg_fmt = msg_fmt;
|
|
|
|
for_each_rawref(warn_if_dangling_symref, &data);
|
2012-04-24 22:45:11 +00:00
|
|
|
}
|
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct string_list *refnames)
|
2012-04-10 05:30:26 +00:00
|
|
|
{
|
2015-11-09 13:34:01 +00:00
|
|
|
struct warn_if_dangling_data data;
|
2012-04-10 05:30:26 +00:00
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
data.fp = fp;
|
|
|
|
data.refname = NULL;
|
|
|
|
data.refnames = refnames;
|
|
|
|
data.msg_fmt = msg_fmt;
|
|
|
|
for_each_rawref(warn_if_dangling_symref, &data);
|
2012-04-10 05:30:26 +00:00
|
|
|
}
|
|
|
|
|
2017-03-26 02:42:34 +00:00
|
|
|
int refs_for_each_tag_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
|
|
|
|
{
|
|
|
|
return refs_for_each_ref_in(refs, "refs/tags/", fn, cb_data);
|
|
|
|
}
|
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
int for_each_tag_ref(each_ref_fn fn, void *cb_data)
|
2012-04-10 05:30:26 +00:00
|
|
|
{
|
2018-04-12 00:21:09 +00:00
|
|
|
return refs_for_each_tag_ref(get_main_ref_store(the_repository), fn, cb_data);
|
2012-04-10 05:30:26 +00:00
|
|
|
}
|
|
|
|
|
2017-03-26 02:42:34 +00:00
|
|
|
int refs_for_each_branch_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
|
|
|
|
{
|
|
|
|
return refs_for_each_ref_in(refs, "refs/heads/", fn, cb_data);
|
2012-04-10 05:30:26 +00:00
|
|
|
}
|
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
int for_each_branch_ref(each_ref_fn fn, void *cb_data)
|
2012-04-10 05:30:26 +00:00
|
|
|
{
|
2018-04-12 00:21:09 +00:00
|
|
|
return refs_for_each_branch_ref(get_main_ref_store(the_repository), fn, cb_data);
|
2012-04-10 05:30:26 +00:00
|
|
|
}
|
|
|
|
|
2017-03-26 02:42:34 +00:00
|
|
|
int refs_for_each_remote_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
|
|
|
|
{
|
|
|
|
return refs_for_each_ref_in(refs, "refs/remotes/", fn, cb_data);
|
2011-12-12 05:38:15 +00:00
|
|
|
}
|
2012-04-10 05:30:26 +00:00
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
int for_each_remote_ref(each_ref_fn fn, void *cb_data)
|
2011-09-29 22:11:42 +00:00
|
|
|
{
|
2018-04-12 00:21:09 +00:00
|
|
|
return refs_for_each_remote_ref(get_main_ref_store(the_repository), fn, cb_data);
|
2011-12-12 05:38:15 +00:00
|
|
|
}
|
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
int head_ref_namespaced(each_ref_fn fn, void *cb_data)
|
|
|
|
{
|
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
int ret = 0;
|
|
|
|
struct object_id oid;
|
|
|
|
int flag;
|
2007-04-17 01:42:50 +00:00
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
strbuf_addf(&buf, "%sHEAD", get_git_namespace());
|
2017-10-15 22:06:56 +00:00
|
|
|
if (!read_ref_full(buf.buf, RESOLVE_REF_READING, &oid, &flag))
|
2015-11-09 13:34:01 +00:00
|
|
|
ret = fn(buf.buf, &oid, flag, cb_data);
|
|
|
|
strbuf_release(&buf);
|
2007-04-17 01:42:50 +00:00
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
return ret;
|
2011-09-29 22:11:42 +00:00
|
|
|
}
|
2007-04-17 01:42:50 +00:00
|
|
|
|
log: add option to choose which refs to decorate
When `log --decorate` is used, git will decorate commits with all
available refs. While in most cases this may give the desired effect,
under some conditions it can lead to excessively verbose output.
Introduce two command line options, `--decorate-refs=<pattern>` and
`--decorate-refs-exclude=<pattern>` to allow the user to select which
refs are used in decoration.
When "--decorate-refs=<pattern>" is given, only the refs that match the
pattern are used in decoration. The refs that match the pattern when
"--decorate-refs-exclude=<pattern>" is given, are never used in
decoration.
These options follow the same convention for mixing negative and
positive patterns across the system, assuming that the inclusive default
is to match all refs available.
(1) if there is no positive pattern given, pretend as if an
inclusive default positive pattern was given;
(2) for each candidate, reject it if it matches no positive
pattern, or if it matches any one of the negative patterns.
The rules for what is considered a match are slightly different from the
rules used elsewhere.
Commands like `log --glob` assume a trailing '/*' when glob chars are
not present in the pattern. This makes it difficult to specify a single
ref. On the other hand, commands like `describe --match --all` allow
specifying exact refs, but do not have the convenience of allowing
"shorthand refs" like 'refs/heads' or 'heads' to refer to
'refs/heads/*'.
The commands introduced in this patch consider a match if:
(a) the pattern contains globs chars,
and regular pattern matching returns a match.
(b) the pattern does not contain glob chars,
and ref '<pattern>' exists, or if ref exists under '<pattern>/'
This allows both behaviours (allowing single refs and shorthand refs)
yet remaining compatible with existent commands.
Helped-by: Kevin Daudt <me@ikke.info>
Helped-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Rafael Ascensão <rafa.almas@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-11-21 21:33:41 +00:00
|
|
|
void normalize_glob_ref(struct string_list_item *item, const char *prefix,
|
|
|
|
const char *pattern)
|
|
|
|
{
|
|
|
|
struct strbuf normalized_pattern = STRBUF_INIT;
|
|
|
|
|
|
|
|
if (*pattern == '/')
|
|
|
|
BUG("pattern must not start with '/'");
|
|
|
|
|
|
|
|
if (prefix) {
|
|
|
|
strbuf_addstr(&normalized_pattern, prefix);
|
|
|
|
}
|
|
|
|
else if (!starts_with(pattern, "refs/"))
|
|
|
|
strbuf_addstr(&normalized_pattern, "refs/");
|
|
|
|
strbuf_addstr(&normalized_pattern, pattern);
|
|
|
|
strbuf_strip_suffix(&normalized_pattern, "/");
|
|
|
|
|
|
|
|
item->string = strbuf_detach(&normalized_pattern, NULL);
|
|
|
|
item->util = has_glob_specials(pattern) ? NULL : item->string;
|
|
|
|
strbuf_release(&normalized_pattern);
|
|
|
|
}
|
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
|
|
|
|
const char *prefix, void *cb_data)
|
2013-04-22 19:52:18 +00:00
|
|
|
{
|
2015-11-09 13:34:01 +00:00
|
|
|
struct strbuf real_pattern = STRBUF_INIT;
|
|
|
|
struct ref_filter filter;
|
|
|
|
int ret;
|
2010-01-20 09:48:25 +00:00
|
|
|
|
2013-11-30 20:55:40 +00:00
|
|
|
if (!prefix && !starts_with(pattern, "refs/"))
|
2010-01-20 09:48:25 +00:00
|
|
|
strbuf_addstr(&real_pattern, "refs/");
|
2010-01-20 09:48:26 +00:00
|
|
|
else if (prefix)
|
|
|
|
strbuf_addstr(&real_pattern, prefix);
|
2010-01-20 09:48:25 +00:00
|
|
|
strbuf_addstr(&real_pattern, pattern);
|
|
|
|
|
2010-03-12 17:04:26 +00:00
|
|
|
if (!has_glob_specials(pattern)) {
|
2010-02-04 05:23:18 +00:00
|
|
|
/* Append implied '/' '*' if not present. */
|
use strbuf_complete to conditionally append slash
When working with paths in strbufs, we frequently want to
ensure that a directory contains a trailing slash before
appending to it. We can shorten this code (and make the
intent more obvious) by calling strbuf_complete.
Most of these cases are trivially identical conversions, but
there are two things to note:
- in a few cases we did not check that the strbuf is
non-empty (which would lead to an out-of-bounds memory
access). These were generally not triggerable in
practice, either from earlier assertions, or typically
because we would have just fed the strbuf to opendir(),
which would choke on an empty path.
- in a few cases we indexed the buffer with "original_len"
or similar, rather than the current sb->len, and it is
not immediately obvious from the diff that they are the
same. In all of these cases, I manually verified that
the strbuf does not change between the assignment and
the strbuf_complete call.
This does not convert cases which look like:
if (sb->len && !is_dir_sep(sb->buf[sb->len - 1]))
strbuf_addch(sb, '/');
as those are obviously semantically different. Some of these
cases arguably should be doing that, but that is out of
scope for this change, which aims purely for cleanup with no
behavior change (and at least it will make such sites easier
to find and examine in the future, as we can grep for
strbuf_complete).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-09-24 21:08:35 +00:00
|
|
|
strbuf_complete(&real_pattern, '/');
|
2010-01-20 09:48:25 +00:00
|
|
|
/* No need to check for '*', there is none. */
|
|
|
|
strbuf_addch(&real_pattern, '*');
|
|
|
|
}
|
|
|
|
|
|
|
|
filter.pattern = real_pattern.buf;
|
|
|
|
filter.fn = fn;
|
|
|
|
filter.cb_data = cb_data;
|
|
|
|
ret = for_each_ref(filter_refs, &filter);
|
|
|
|
|
|
|
|
strbuf_release(&real_pattern);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-01-20 09:48:26 +00:00
|
|
|
int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data)
|
|
|
|
{
|
|
|
|
return for_each_glob_ref_in(fn, pattern, NULL, cb_data);
|
|
|
|
}
|
|
|
|
|
2009-05-13 21:22:04 +00:00
|
|
|
const char *prettify_refname(const char *name)
|
2009-03-09 01:06:05 +00:00
|
|
|
{
|
2017-03-23 15:50:12 +00:00
|
|
|
if (skip_prefix(name, "refs/heads/", &name) ||
|
|
|
|
skip_prefix(name, "refs/tags/", &name) ||
|
|
|
|
skip_prefix(name, "refs/remotes/", &name))
|
|
|
|
; /* nothing */
|
|
|
|
return name;
|
2009-03-09 01:06:05 +00:00
|
|
|
}
|
|
|
|
|
2014-01-14 03:16:07 +00:00
|
|
|
static const char *ref_rev_parse_rules[] = {
|
add refname_match()
We use at least two rulesets for matching abbreviated refnames with
full refnames (starting with 'refs/'). git-rev-parse and git-fetch
use slightly different rules.
This commit introduces a new function refname_match
(const char *abbrev_name, const char *full_name, const char **rules).
abbrev_name is expanded using the rules and matched against full_name.
If a match is found the function returns true. rules is a NULL-terminate
list of format patterns with "%.*s", for example:
const char *ref_rev_parse_rules[] = {
"%.*s",
"refs/%.*s",
"refs/tags/%.*s",
"refs/heads/%.*s",
"refs/remotes/%.*s",
"refs/remotes/%.*s/HEAD",
NULL
};
Asterisks are included in the format strings because this is the form
required in sha1_name.c. Sharing the list with the functions there is
a good idea to avoid duplicating the rules. Hopefully this
facilitates unified matching rules in the future.
This commit makes the rules used by rev-parse for resolving refs to
sha1s available for string comparison. Before this change, the rules
were buried in get_sha1*() and dwim_ref().
A follow-up commit will refactor the rules used by fetch.
refname_match() will be used for matching refspecs in git-send-pack.
Thanks to Daniel Barkalow <barkalow@iabervon.org> for pointing
out that ref_matches_abbrev in remote.c solves a similar problem
and care should be taken to avoid confusion.
Signed-off-by: Steffen Prohaska <prohaska@zib.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2007-11-11 14:01:46 +00:00
|
|
|
"%.*s",
|
|
|
|
"refs/%.*s",
|
|
|
|
"refs/tags/%.*s",
|
|
|
|
"refs/heads/%.*s",
|
|
|
|
"refs/remotes/%.*s",
|
|
|
|
"refs/remotes/%.*s/HEAD",
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
remote: make refspec follow the same disambiguation rule as local refs
When matching a non-wildcard LHS of a refspec against a list of
refs, find_ref_by_name_abbrev() returns the first ref that matches
using any DWIM rules used by refname_match() in refs.c, even if a
better match occurs later in the list of refs.
This causes unexpected behavior when (for example) fetching using
the refspec "refs/heads/s:<something>" from a remote with both
"refs/heads/refs/heads/s" and "refs/heads/s"; even if the former was
inadvertently created, one would still expect the latter to be
fetched. Similarly, when both a tag T and a branch T exist,
fetching T should favor the tag, just like how local refname
disambiguation rule works. But because the code walks over
ls-remote output from the remote, which happens to be sorted in
alphabetical order and has refs/heads/T before refs/tags/T, a
request to fetch T is (mis)interpreted as fetching refs/heads/T.
Update refname_match(), all of whose current callers care only if it
returns non-zero (i.e. matches) to see if an abbreviated name can
mean the full name being tested, so that it returns a positive
integer whose magnitude can be used to tell the precedence, and fix
the find_ref_by_name_abbrev() function not to stop at the first
match but find the match with the highest precedence.
This is based on an earlier work, which special cased only the exact
matches, by Jonathan Tan.
Helped-by: Jonathan Tan <jonathantanmy@google.com>
Helped-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-08-01 16:22:37 +00:00
|
|
|
#define NUM_REV_PARSE_RULES (ARRAY_SIZE(ref_rev_parse_rules) - 1)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Is it possible that the caller meant full_name with abbrev_name?
|
|
|
|
* If so return a non-zero value to signal "yes"; the magnitude of
|
|
|
|
* the returned value gives the precedence used for disambiguation.
|
|
|
|
*
|
|
|
|
* If abbrev_name cannot mean full_name, return 0.
|
|
|
|
*/
|
2014-01-14 03:16:07 +00:00
|
|
|
int refname_match(const char *abbrev_name, const char *full_name)
|
add refname_match()
We use at least two rulesets for matching abbreviated refnames with
full refnames (starting with 'refs/'). git-rev-parse and git-fetch
use slightly different rules.
This commit introduces a new function refname_match
(const char *abbrev_name, const char *full_name, const char **rules).
abbrev_name is expanded using the rules and matched against full_name.
If a match is found the function returns true. rules is a NULL-terminate
list of format patterns with "%.*s", for example:
const char *ref_rev_parse_rules[] = {
"%.*s",
"refs/%.*s",
"refs/tags/%.*s",
"refs/heads/%.*s",
"refs/remotes/%.*s",
"refs/remotes/%.*s/HEAD",
NULL
};
Asterisks are included in the format strings because this is the form
required in sha1_name.c. Sharing the list with the functions there is
a good idea to avoid duplicating the rules. Hopefully this
facilitates unified matching rules in the future.
This commit makes the rules used by rev-parse for resolving refs to
sha1s available for string comparison. Before this change, the rules
were buried in get_sha1*() and dwim_ref().
A follow-up commit will refactor the rules used by fetch.
refname_match() will be used for matching refspecs in git-send-pack.
Thanks to Daniel Barkalow <barkalow@iabervon.org> for pointing
out that ref_matches_abbrev in remote.c solves a similar problem
and care should be taken to avoid confusion.
Signed-off-by: Steffen Prohaska <prohaska@zib.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2007-11-11 14:01:46 +00:00
|
|
|
{
|
|
|
|
const char **p;
|
|
|
|
const int abbrev_name_len = strlen(abbrev_name);
|
remote: make refspec follow the same disambiguation rule as local refs
When matching a non-wildcard LHS of a refspec against a list of
refs, find_ref_by_name_abbrev() returns the first ref that matches
using any DWIM rules used by refname_match() in refs.c, even if a
better match occurs later in the list of refs.
This causes unexpected behavior when (for example) fetching using
the refspec "refs/heads/s:<something>" from a remote with both
"refs/heads/refs/heads/s" and "refs/heads/s"; even if the former was
inadvertently created, one would still expect the latter to be
fetched. Similarly, when both a tag T and a branch T exist,
fetching T should favor the tag, just like how local refname
disambiguation rule works. But because the code walks over
ls-remote output from the remote, which happens to be sorted in
alphabetical order and has refs/heads/T before refs/tags/T, a
request to fetch T is (mis)interpreted as fetching refs/heads/T.
Update refname_match(), all of whose current callers care only if it
returns non-zero (i.e. matches) to see if an abbreviated name can
mean the full name being tested, so that it returns a positive
integer whose magnitude can be used to tell the precedence, and fix
the find_ref_by_name_abbrev() function not to stop at the first
match but find the match with the highest precedence.
This is based on an earlier work, which special cased only the exact
matches, by Jonathan Tan.
Helped-by: Jonathan Tan <jonathantanmy@google.com>
Helped-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-08-01 16:22:37 +00:00
|
|
|
const int num_rules = NUM_REV_PARSE_RULES;
|
add refname_match()
We use at least two rulesets for matching abbreviated refnames with
full refnames (starting with 'refs/'). git-rev-parse and git-fetch
use slightly different rules.
This commit introduces a new function refname_match
(const char *abbrev_name, const char *full_name, const char **rules).
abbrev_name is expanded using the rules and matched against full_name.
If a match is found the function returns true. rules is a NULL-terminate
list of format patterns with "%.*s", for example:
const char *ref_rev_parse_rules[] = {
"%.*s",
"refs/%.*s",
"refs/tags/%.*s",
"refs/heads/%.*s",
"refs/remotes/%.*s",
"refs/remotes/%.*s/HEAD",
NULL
};
Asterisks are included in the format strings because this is the form
required in sha1_name.c. Sharing the list with the functions there is
a good idea to avoid duplicating the rules. Hopefully this
facilitates unified matching rules in the future.
This commit makes the rules used by rev-parse for resolving refs to
sha1s available for string comparison. Before this change, the rules
were buried in get_sha1*() and dwim_ref().
A follow-up commit will refactor the rules used by fetch.
refname_match() will be used for matching refspecs in git-send-pack.
Thanks to Daniel Barkalow <barkalow@iabervon.org> for pointing
out that ref_matches_abbrev in remote.c solves a similar problem
and care should be taken to avoid confusion.
Signed-off-by: Steffen Prohaska <prohaska@zib.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2007-11-11 14:01:46 +00:00
|
|
|
|
remote: make refspec follow the same disambiguation rule as local refs
When matching a non-wildcard LHS of a refspec against a list of
refs, find_ref_by_name_abbrev() returns the first ref that matches
using any DWIM rules used by refname_match() in refs.c, even if a
better match occurs later in the list of refs.
This causes unexpected behavior when (for example) fetching using
the refspec "refs/heads/s:<something>" from a remote with both
"refs/heads/refs/heads/s" and "refs/heads/s"; even if the former was
inadvertently created, one would still expect the latter to be
fetched. Similarly, when both a tag T and a branch T exist,
fetching T should favor the tag, just like how local refname
disambiguation rule works. But because the code walks over
ls-remote output from the remote, which happens to be sorted in
alphabetical order and has refs/heads/T before refs/tags/T, a
request to fetch T is (mis)interpreted as fetching refs/heads/T.
Update refname_match(), all of whose current callers care only if it
returns non-zero (i.e. matches) to see if an abbreviated name can
mean the full name being tested, so that it returns a positive
integer whose magnitude can be used to tell the precedence, and fix
the find_ref_by_name_abbrev() function not to stop at the first
match but find the match with the highest precedence.
This is based on an earlier work, which special cased only the exact
matches, by Jonathan Tan.
Helped-by: Jonathan Tan <jonathantanmy@google.com>
Helped-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-08-01 16:22:37 +00:00
|
|
|
for (p = ref_rev_parse_rules; *p; p++)
|
|
|
|
if (!strcmp(full_name, mkpath(*p, abbrev_name_len, abbrev_name)))
|
|
|
|
return &ref_rev_parse_rules[num_rules] - p;
|
add refname_match()
We use at least two rulesets for matching abbreviated refnames with
full refnames (starting with 'refs/'). git-rev-parse and git-fetch
use slightly different rules.
This commit introduces a new function refname_match
(const char *abbrev_name, const char *full_name, const char **rules).
abbrev_name is expanded using the rules and matched against full_name.
If a match is found the function returns true. rules is a NULL-terminate
list of format patterns with "%.*s", for example:
const char *ref_rev_parse_rules[] = {
"%.*s",
"refs/%.*s",
"refs/tags/%.*s",
"refs/heads/%.*s",
"refs/remotes/%.*s",
"refs/remotes/%.*s/HEAD",
NULL
};
Asterisks are included in the format strings because this is the form
required in sha1_name.c. Sharing the list with the functions there is
a good idea to avoid duplicating the rules. Hopefully this
facilitates unified matching rules in the future.
This commit makes the rules used by rev-parse for resolving refs to
sha1s available for string comparison. Before this change, the rules
were buried in get_sha1*() and dwim_ref().
A follow-up commit will refactor the rules used by fetch.
refname_match() will be used for matching refspecs in git-send-pack.
Thanks to Daniel Barkalow <barkalow@iabervon.org> for pointing
out that ref_matches_abbrev in remote.c solves a similar problem
and care should be taken to avoid confusion.
Signed-off-by: Steffen Prohaska <prohaska@zib.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2007-11-11 14:01:46 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-03-15 17:31:24 +00:00
|
|
|
/*
|
|
|
|
* Given a 'prefix' expand it by the rules in 'ref_rev_parse_rules' and add
|
|
|
|
* the results to 'prefixes'
|
|
|
|
*/
|
|
|
|
void expand_ref_prefix(struct argv_array *prefixes, const char *prefix)
|
|
|
|
{
|
|
|
|
const char **p;
|
|
|
|
int len = strlen(prefix);
|
|
|
|
|
|
|
|
for (p = ref_rev_parse_rules; *p; p++)
|
|
|
|
argv_array_pushf(prefixes, *p, len, prefix);
|
|
|
|
}
|
|
|
|
|
2011-10-12 17:35:38 +00:00
|
|
|
/*
|
|
|
|
* *string and *len will only be substituted, and *string returned (for
|
|
|
|
* later free()ing) if the string passed in is a magic short-hand form
|
|
|
|
* to name a branch.
|
|
|
|
*/
|
|
|
|
static char *substitute_branch_name(const char **string, int *len)
|
|
|
|
{
|
|
|
|
struct strbuf buf = STRBUF_INIT;
|
interpret_branch_name: allow callers to restrict expansions
The interpret_branch_name() function converts names like
@{-1} and @{upstream} into branch names. The expanded ref
names are not fully qualified, and may be outside of the
refs/heads/ namespace (e.g., "@" expands to "HEAD", and
"@{upstream}" is likely to be in "refs/remotes/").
This is OK for callers like dwim_ref() which are primarily
interested in resolving the resulting name, no matter where
it is. But callers like "git branch" treat the result as a
branch name in refs/heads/. When we expand to a ref outside
that namespace, the results are very confusing (e.g., "git
branch @" tries to create refs/heads/HEAD, which is
nonsense).
Callers can't know from the returned string how the
expansion happened (e.g., did the user really ask for a
branch named "HEAD", or did we do a bogus expansion?). One
fix would be to return some out-parameters describing the
types of expansion that occurred. This has the benefit that
the caller can generate precise error messages ("I
understood @{upstream} to mean origin/master, but that is a
remote tracking branch, so you cannot create it as a local
name").
However, out-parameters make the function interface somewhat
cumbersome. Instead, let's do the opposite: let the caller
tell us which elements to expand. That's easier to pass in,
and none of the callers give more precise error messages
than "@{upstream} isn't a valid branch name" anyway (which
should be sufficient).
The strbuf_branchname() function needs a similar parameter,
as most of the callers access interpret_branch_name()
through it.
We can break the callers down into two groups:
1. Callers that are happy with any kind of ref in the
result. We pass "0" here, so they continue to work
without restrictions. This includes merge_name(),
the reflog handling in add_pending_object_with_path(),
and substitute_branch_name(). This last is what powers
dwim_ref().
2. Callers that have funny corner cases (mostly in
git-branch and git-checkout). These need to make use of
the new parameter, but I've left them as "0" in this
patch, and will address them individually in follow-on
patches.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-03-02 08:23:01 +00:00
|
|
|
int ret = interpret_branch_name(*string, *len, &buf, 0);
|
2011-10-12 17:35:38 +00:00
|
|
|
|
|
|
|
if (ret == *len) {
|
|
|
|
size_t size;
|
|
|
|
*string = strbuf_detach(&buf, &size);
|
|
|
|
*len = size;
|
|
|
|
return (char *)*string;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-10-15 22:06:57 +00:00
|
|
|
int dwim_ref(const char *str, int len, struct object_id *oid, char **ref)
|
2011-10-12 17:35:38 +00:00
|
|
|
{
|
|
|
|
char *last_branch = substitute_branch_name(&str, &len);
|
2017-10-15 22:06:57 +00:00
|
|
|
int refs_found = expand_ref(str, len, oid, ref);
|
2016-06-12 10:54:02 +00:00
|
|
|
free(last_branch);
|
|
|
|
return refs_found;
|
|
|
|
}
|
|
|
|
|
2017-10-15 22:06:57 +00:00
|
|
|
int expand_ref(const char *str, int len, struct object_id *oid, char **ref)
|
2016-06-12 10:54:02 +00:00
|
|
|
{
|
2011-10-12 17:35:38 +00:00
|
|
|
const char **p, *r;
|
|
|
|
int refs_found = 0;
|
2017-03-28 19:46:33 +00:00
|
|
|
struct strbuf fullref = STRBUF_INIT;
|
2011-10-12 17:35:38 +00:00
|
|
|
|
|
|
|
*ref = NULL;
|
|
|
|
for (p = ref_rev_parse_rules; *p; p++) {
|
2017-10-15 22:06:57 +00:00
|
|
|
struct object_id oid_from_ref;
|
|
|
|
struct object_id *this_result;
|
2011-10-12 17:35:38 +00:00
|
|
|
int flag;
|
|
|
|
|
2017-10-15 22:06:57 +00:00
|
|
|
this_result = refs_found ? &oid_from_ref : oid;
|
2017-03-28 19:46:33 +00:00
|
|
|
strbuf_reset(&fullref);
|
|
|
|
strbuf_addf(&fullref, *p, len, str);
|
|
|
|
r = resolve_ref_unsafe(fullref.buf, RESOLVE_REF_READING,
|
refs: convert resolve_ref_unsafe to struct object_id
Convert resolve_ref_unsafe to take a pointer to struct object_id by
converting one remaining caller to use struct object_id, removing the
temporary NULL pointer check in expand_ref, converting the declaration
and definition, and applying the following semantic patch:
@@
expression E1, E2, E3, E4;
@@
- resolve_ref_unsafe(E1, E2, E3.hash, E4)
+ resolve_ref_unsafe(E1, E2, &E3, E4)
@@
expression E1, E2, E3, E4;
@@
- resolve_ref_unsafe(E1, E2, E3->hash, E4)
+ resolve_ref_unsafe(E1, E2, E3, E4)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-10-15 22:07:09 +00:00
|
|
|
this_result, &flag);
|
2011-10-12 17:35:38 +00:00
|
|
|
if (r) {
|
|
|
|
if (!refs_found++)
|
|
|
|
*ref = xstrdup(r);
|
|
|
|
if (!warn_ambiguous_refs)
|
|
|
|
break;
|
2017-03-28 19:46:33 +00:00
|
|
|
} else if ((flag & REF_ISSYMREF) && strcmp(fullref.buf, "HEAD")) {
|
2018-07-21 07:49:35 +00:00
|
|
|
warning(_("ignoring dangling symref %s"), fullref.buf);
|
2017-03-28 19:46:33 +00:00
|
|
|
} else if ((flag & REF_ISBROKEN) && strchr(fullref.buf, '/')) {
|
2018-07-21 07:49:35 +00:00
|
|
|
warning(_("ignoring broken ref %s"), fullref.buf);
|
2011-10-19 20:55:49 +00:00
|
|
|
}
|
2011-10-12 17:35:38 +00:00
|
|
|
}
|
2017-03-28 19:46:33 +00:00
|
|
|
strbuf_release(&fullref);
|
2011-10-12 17:35:38 +00:00
|
|
|
return refs_found;
|
|
|
|
}
|
|
|
|
|
2017-10-15 22:06:59 +00:00
|
|
|
int dwim_log(const char *str, int len, struct object_id *oid, char **log)
|
2011-10-12 17:35:38 +00:00
|
|
|
{
|
|
|
|
char *last_branch = substitute_branch_name(&str, &len);
|
|
|
|
const char **p;
|
|
|
|
int logs_found = 0;
|
2017-03-28 19:46:33 +00:00
|
|
|
struct strbuf path = STRBUF_INIT;
|
2011-10-12 17:35:38 +00:00
|
|
|
|
|
|
|
*log = NULL;
|
|
|
|
for (p = ref_rev_parse_rules; *p; p++) {
|
2017-10-15 22:06:59 +00:00
|
|
|
struct object_id hash;
|
2011-10-12 17:35:38 +00:00
|
|
|
const char *ref, *it;
|
|
|
|
|
2017-03-28 19:46:33 +00:00
|
|
|
strbuf_reset(&path);
|
|
|
|
strbuf_addf(&path, *p, len, str);
|
|
|
|
ref = resolve_ref_unsafe(path.buf, RESOLVE_REF_READING,
|
refs: convert resolve_ref_unsafe to struct object_id
Convert resolve_ref_unsafe to take a pointer to struct object_id by
converting one remaining caller to use struct object_id, removing the
temporary NULL pointer check in expand_ref, converting the declaration
and definition, and applying the following semantic patch:
@@
expression E1, E2, E3, E4;
@@
- resolve_ref_unsafe(E1, E2, E3.hash, E4)
+ resolve_ref_unsafe(E1, E2, &E3, E4)
@@
expression E1, E2, E3, E4;
@@
- resolve_ref_unsafe(E1, E2, E3->hash, E4)
+ resolve_ref_unsafe(E1, E2, E3, E4)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-10-15 22:07:09 +00:00
|
|
|
&hash, NULL);
|
2011-10-12 17:35:38 +00:00
|
|
|
if (!ref)
|
|
|
|
continue;
|
2017-03-28 19:46:33 +00:00
|
|
|
if (reflog_exists(path.buf))
|
|
|
|
it = path.buf;
|
|
|
|
else if (strcmp(ref, path.buf) && reflog_exists(ref))
|
2011-10-12 17:35:38 +00:00
|
|
|
it = ref;
|
|
|
|
else
|
|
|
|
continue;
|
|
|
|
if (!logs_found++) {
|
|
|
|
*log = xstrdup(it);
|
2017-10-15 22:06:59 +00:00
|
|
|
oidcpy(oid, &hash);
|
2011-10-12 17:35:38 +00:00
|
|
|
}
|
2015-11-09 13:34:01 +00:00
|
|
|
if (!warn_ambiguous_refs)
|
|
|
|
break;
|
2006-09-30 22:02:00 +00:00
|
|
|
}
|
2017-03-28 19:46:33 +00:00
|
|
|
strbuf_release(&path);
|
2015-11-09 13:34:01 +00:00
|
|
|
free(last_branch);
|
|
|
|
return logs_found;
|
2013-09-04 15:22:41 +00:00
|
|
|
}
|
|
|
|
|
2015-07-31 06:06:18 +00:00
|
|
|
static int is_per_worktree_ref(const char *refname)
|
|
|
|
{
|
2015-09-01 02:13:11 +00:00
|
|
|
return !strcmp(refname, "HEAD") ||
|
2018-04-25 12:29:16 +00:00
|
|
|
starts_with(refname, "refs/bisect/") ||
|
|
|
|
starts_with(refname, "refs/rewritten/");
|
2015-07-31 06:06:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int is_pseudoref_syntax(const char *refname)
|
|
|
|
{
|
|
|
|
const char *c;
|
|
|
|
|
|
|
|
for (c = refname; *c; c++) {
|
|
|
|
if (!isupper(*c) && *c != '-' && *c != '_')
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
enum ref_type ref_type(const char *refname)
|
|
|
|
{
|
|
|
|
if (is_per_worktree_ref(refname))
|
|
|
|
return REF_TYPE_PER_WORKTREE;
|
|
|
|
if (is_pseudoref_syntax(refname))
|
|
|
|
return REF_TYPE_PSEUDOREF;
|
|
|
|
return REF_TYPE_NORMAL;
|
|
|
|
}
|
|
|
|
|
2017-08-21 11:51:34 +00:00
|
|
|
long get_files_ref_lock_timeout_ms(void)
|
|
|
|
{
|
|
|
|
static int configured = 0;
|
|
|
|
|
|
|
|
/* The default timeout is 100 ms: */
|
|
|
|
static int timeout_ms = 100;
|
|
|
|
|
|
|
|
if (!configured) {
|
|
|
|
git_config_get_int("core.filesreflocktimeout", &timeout_ms);
|
|
|
|
configured = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return timeout_ms;
|
|
|
|
}
|
|
|
|
|
2017-10-15 22:06:51 +00:00
|
|
|
static int write_pseudoref(const char *pseudoref, const struct object_id *oid,
|
|
|
|
const struct object_id *old_oid, struct strbuf *err)
|
2015-07-31 06:06:19 +00:00
|
|
|
{
|
|
|
|
const char *filename;
|
|
|
|
int fd;
|
2018-05-09 20:55:36 +00:00
|
|
|
struct lock_file lock = LOCK_INIT;
|
2015-07-31 06:06:19 +00:00
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
int ret = -1;
|
|
|
|
|
2017-10-15 22:06:52 +00:00
|
|
|
if (!oid)
|
|
|
|
return 0;
|
|
|
|
|
2017-10-15 22:06:51 +00:00
|
|
|
strbuf_addf(&buf, "%s\n", oid_to_hex(oid));
|
2015-07-31 06:06:19 +00:00
|
|
|
|
|
|
|
filename = git_path("%s", pseudoref);
|
2018-05-09 20:55:36 +00:00
|
|
|
fd = hold_lock_file_for_update_timeout(&lock, filename, 0,
|
2017-08-21 11:51:34 +00:00
|
|
|
get_files_ref_lock_timeout_ms());
|
2015-07-31 06:06:19 +00:00
|
|
|
if (fd < 0) {
|
2018-07-21 07:49:35 +00:00
|
|
|
strbuf_addf(err, _("could not open '%s' for writing: %s"),
|
2015-07-31 06:06:19 +00:00
|
|
|
filename, strerror(errno));
|
2017-08-30 17:58:12 +00:00
|
|
|
goto done;
|
2015-07-31 06:06:19 +00:00
|
|
|
}
|
|
|
|
|
2017-10-15 22:06:51 +00:00
|
|
|
if (old_oid) {
|
|
|
|
struct object_id actual_old_oid;
|
2015-07-15 22:05:28 +00:00
|
|
|
|
2018-05-10 19:29:56 +00:00
|
|
|
if (read_ref(pseudoref, &actual_old_oid)) {
|
|
|
|
if (!is_null_oid(old_oid)) {
|
2018-07-21 07:49:35 +00:00
|
|
|
strbuf_addf(err, _("could not read ref '%s'"),
|
2018-05-10 19:29:56 +00:00
|
|
|
pseudoref);
|
|
|
|
rollback_lock_file(&lock);
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
} else if (is_null_oid(old_oid)) {
|
2018-07-21 07:49:35 +00:00
|
|
|
strbuf_addf(err, _("ref '%s' already exists"),
|
2018-05-10 19:29:56 +00:00
|
|
|
pseudoref);
|
|
|
|
rollback_lock_file(&lock);
|
|
|
|
goto done;
|
2018-08-28 21:22:48 +00:00
|
|
|
} else if (!oideq(&actual_old_oid, old_oid)) {
|
2018-07-21 07:49:35 +00:00
|
|
|
strbuf_addf(err, _("unexpected object ID when writing '%s'"),
|
2018-05-10 19:29:54 +00:00
|
|
|
pseudoref);
|
2015-07-31 06:06:19 +00:00
|
|
|
rollback_lock_file(&lock);
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
avoid "write_in_full(fd, buf, len) != len" pattern
The return value of write_in_full() is either "-1", or the
requested number of bytes[1]. If we make a partial write
before seeing an error, we still return -1, not a partial
value. This goes back to f6aa66cb95 (write_in_full: really
write in full or return error on disk full., 2007-01-11).
So checking anything except "was the return value negative"
is pointless. And there are a couple of reasons not to do
so:
1. It can do a funny signed/unsigned comparison. If your
"len" is signed (e.g., a size_t) then the compiler will
promote the "-1" to its unsigned variant.
This works out for "!= len" (unless you really were
trying to write the maximum size_t bytes), but is a
bug if you check "< len" (an example of which was fixed
recently in config.c).
We should avoid promoting the mental model that you
need to check the length at all, so that new sites are
not tempted to copy us.
2. Checking for a negative value is shorter to type,
especially when the length is an expression.
3. Linus says so. In d34cf19b89 (Clean up write_in_full()
users, 2007-01-11), right after the write_in_full()
semantics were changed, he wrote:
I really wish every "write_in_full()" user would just
check against "<0" now, but this fixes the nasty and
stupid ones.
Appeals to authority aside, this makes it clear that
writing it this way does not have an intentional
benefit. It's a historical curiosity that we never
bothered to clean up (and which was undoubtedly
cargo-culted into new sites).
So let's convert these obviously-correct cases (this
includes write_str_in_full(), which is just a wrapper for
write_in_full()).
[1] A careful reader may notice there is one way that
write_in_full() can return a different value. If we ask
write() to write N bytes and get a return value that is
_larger_ than N, we could return a larger total. But
besides the fact that this would imply a totally broken
version of write(), it would already invoke undefined
behavior. Our internal remaining counter is an unsigned
size_t, which means that subtracting too many byte will
wrap it around to a very large number. So we'll instantly
begin reading off the end of the buffer, trying to write
gigabytes (or petabytes) of data.
Signed-off-by: Jeff King <peff@peff.net>
Reviewed-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-09-13 17:16:03 +00:00
|
|
|
if (write_in_full(fd, buf.buf, buf.len) < 0) {
|
2018-07-21 07:49:35 +00:00
|
|
|
strbuf_addf(err, _("could not write to '%s'"), filename);
|
2015-07-31 06:06:19 +00:00
|
|
|
rollback_lock_file(&lock);
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
commit_lock_file(&lock);
|
|
|
|
ret = 0;
|
|
|
|
done:
|
|
|
|
strbuf_release(&buf);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-10-15 22:06:50 +00:00
|
|
|
static int delete_pseudoref(const char *pseudoref, const struct object_id *old_oid)
|
2015-07-31 06:06:19 +00:00
|
|
|
{
|
|
|
|
const char *filename;
|
|
|
|
|
|
|
|
filename = git_path("%s", pseudoref);
|
|
|
|
|
2017-10-15 22:06:50 +00:00
|
|
|
if (old_oid && !is_null_oid(old_oid)) {
|
refs.c: do not die if locking fails in `delete_pseudoref()`
After taking the lock we check whether we got it and die otherwise. But
since we take the lock using `LOCK_DIE_ON_ERROR`, we would already have
died.
Considering the choice between dropping the dead code and dropping the
flag, let's go for option number three: Drop the flag, write an error
instead of dying, then return -1. This function already returns -1 for
another error, so the caller (or rather, its callers) should be able to
handle this. There is some inconsistency around how we handle errors in
this function and elsewhere in this file, but let's take this small step
towards gentle error-reporting now and leave the rest for another time.
While at it, make the lock non-static and reduce its scope. (Placing
`struct lock_file`s on the stack used to be a bad idea, because the
temp- and lockfile-machinery would keep a pointer into the struct. But
after 076aa2cbd (tempfile: auto-allocate tempfiles on heap, 2017-09-05),
we can safely have lockfiles on the stack.)
Signed-off-by: Martin Ågren <martin.agren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-05-09 20:55:37 +00:00
|
|
|
struct lock_file lock = LOCK_INIT;
|
2015-07-31 06:06:19 +00:00
|
|
|
int fd;
|
2017-10-15 22:06:50 +00:00
|
|
|
struct object_id actual_old_oid;
|
2015-07-31 06:06:19 +00:00
|
|
|
|
2017-08-21 11:51:34 +00:00
|
|
|
fd = hold_lock_file_for_update_timeout(
|
refs.c: do not die if locking fails in `delete_pseudoref()`
After taking the lock we check whether we got it and die otherwise. But
since we take the lock using `LOCK_DIE_ON_ERROR`, we would already have
died.
Considering the choice between dropping the dead code and dropping the
flag, let's go for option number three: Drop the flag, write an error
instead of dying, then return -1. This function already returns -1 for
another error, so the caller (or rather, its callers) should be able to
handle this. There is some inconsistency around how we handle errors in
this function and elsewhere in this file, but let's take this small step
towards gentle error-reporting now and leave the rest for another time.
While at it, make the lock non-static and reduce its scope. (Placing
`struct lock_file`s on the stack used to be a bad idea, because the
temp- and lockfile-machinery would keep a pointer into the struct. But
after 076aa2cbd (tempfile: auto-allocate tempfiles on heap, 2017-09-05),
we can safely have lockfiles on the stack.)
Signed-off-by: Martin Ågren <martin.agren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-05-09 20:55:37 +00:00
|
|
|
&lock, filename, 0,
|
2017-08-21 11:51:34 +00:00
|
|
|
get_files_ref_lock_timeout_ms());
|
refs.c: do not die if locking fails in `delete_pseudoref()`
After taking the lock we check whether we got it and die otherwise. But
since we take the lock using `LOCK_DIE_ON_ERROR`, we would already have
died.
Considering the choice between dropping the dead code and dropping the
flag, let's go for option number three: Drop the flag, write an error
instead of dying, then return -1. This function already returns -1 for
another error, so the caller (or rather, its callers) should be able to
handle this. There is some inconsistency around how we handle errors in
this function and elsewhere in this file, but let's take this small step
towards gentle error-reporting now and leave the rest for another time.
While at it, make the lock non-static and reduce its scope. (Placing
`struct lock_file`s on the stack used to be a bad idea, because the
temp- and lockfile-machinery would keep a pointer into the struct. But
after 076aa2cbd (tempfile: auto-allocate tempfiles on heap, 2017-09-05),
we can safely have lockfiles on the stack.)
Signed-off-by: Martin Ågren <martin.agren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-05-09 20:55:37 +00:00
|
|
|
if (fd < 0) {
|
|
|
|
error_errno(_("could not open '%s' for writing"),
|
|
|
|
filename);
|
|
|
|
return -1;
|
|
|
|
}
|
2017-10-15 22:06:56 +00:00
|
|
|
if (read_ref(pseudoref, &actual_old_oid))
|
2018-07-21 07:49:35 +00:00
|
|
|
die(_("could not read ref '%s'"), pseudoref);
|
2018-08-28 21:22:48 +00:00
|
|
|
if (!oideq(&actual_old_oid, old_oid)) {
|
2018-07-21 07:49:35 +00:00
|
|
|
error(_("unexpected object ID when deleting '%s'"),
|
2018-05-10 19:29:54 +00:00
|
|
|
pseudoref);
|
2015-07-31 06:06:19 +00:00
|
|
|
rollback_lock_file(&lock);
|
|
|
|
return -1;
|
2015-07-21 21:04:50 +00:00
|
|
|
}
|
2015-07-31 06:06:19 +00:00
|
|
|
|
|
|
|
unlink(filename);
|
|
|
|
rollback_lock_file(&lock);
|
|
|
|
} else {
|
|
|
|
unlink(filename);
|
2006-05-17 09:55:02 +00:00
|
|
|
}
|
2015-07-21 21:04:50 +00:00
|
|
|
|
2006-05-17 09:55:02 +00:00
|
|
|
return 0;
|
2005-06-06 20:31:29 +00:00
|
|
|
}
|
2006-05-17 09:56:09 +00:00
|
|
|
|
2017-03-26 02:42:35 +00:00
|
|
|
int refs_delete_ref(struct ref_store *refs, const char *msg,
|
|
|
|
const char *refname,
|
2017-10-15 22:06:50 +00:00
|
|
|
const struct object_id *old_oid,
|
2017-03-26 02:42:35 +00:00
|
|
|
unsigned int flags)
|
2007-01-26 22:26:09 +00:00
|
|
|
{
|
2014-04-30 16:22:45 +00:00
|
|
|
struct ref_transaction *transaction;
|
2015-07-21 21:04:50 +00:00
|
|
|
struct strbuf err = STRBUF_INIT;
|
2007-01-26 22:26:10 +00:00
|
|
|
|
2017-03-26 02:42:35 +00:00
|
|
|
if (ref_type(refname) == REF_TYPE_PSEUDOREF) {
|
2018-04-12 00:21:09 +00:00
|
|
|
assert(refs == get_main_ref_store(the_repository));
|
2017-10-15 22:06:50 +00:00
|
|
|
return delete_pseudoref(refname, old_oid);
|
2017-03-26 02:42:35 +00:00
|
|
|
}
|
2007-02-08 07:41:43 +00:00
|
|
|
|
2017-03-26 02:42:35 +00:00
|
|
|
transaction = ref_store_transaction_begin(refs, &err);
|
2014-04-30 16:22:45 +00:00
|
|
|
if (!transaction ||
|
2017-10-15 22:06:53 +00:00
|
|
|
ref_transaction_delete(transaction, refname, old_oid,
|
2017-02-21 01:10:32 +00:00
|
|
|
flags, msg, &err) ||
|
2014-04-30 19:22:42 +00:00
|
|
|
ref_transaction_commit(transaction, &err)) {
|
2014-04-30 16:22:45 +00:00
|
|
|
error("%s", err.buf);
|
|
|
|
ref_transaction_free(transaction);
|
|
|
|
strbuf_release(&err);
|
2006-09-30 22:02:00 +00:00
|
|
|
return 1;
|
2007-01-26 22:26:09 +00:00
|
|
|
}
|
2015-11-09 13:34:01 +00:00
|
|
|
ref_transaction_free(transaction);
|
|
|
|
strbuf_release(&err);
|
2008-01-16 19:14:30 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2007-01-26 22:26:09 +00:00
|
|
|
|
2017-03-26 02:42:35 +00:00
|
|
|
int delete_ref(const char *msg, const char *refname,
|
2017-10-15 22:06:50 +00:00
|
|
|
const struct object_id *old_oid, unsigned int flags)
|
2017-03-26 02:42:35 +00:00
|
|
|
{
|
2018-04-12 00:21:09 +00:00
|
|
|
return refs_delete_ref(get_main_ref_store(the_repository), msg, refname,
|
2017-10-15 22:06:50 +00:00
|
|
|
old_oid, flags);
|
2017-03-26 02:42:35 +00:00
|
|
|
}
|
|
|
|
|
2018-07-10 21:08:22 +00:00
|
|
|
void copy_reflog_msg(struct strbuf *sb, const char *msg)
|
2007-07-29 00:17:17 +00:00
|
|
|
{
|
|
|
|
char c;
|
|
|
|
int wasspace = 1;
|
2007-01-26 22:26:10 +00:00
|
|
|
|
2018-07-10 21:08:22 +00:00
|
|
|
strbuf_addch(sb, '\t');
|
2007-07-29 00:17:17 +00:00
|
|
|
while ((c = *msg++)) {
|
|
|
|
if (wasspace && isspace(c))
|
|
|
|
continue;
|
|
|
|
wasspace = isspace(c);
|
|
|
|
if (wasspace)
|
|
|
|
c = ' ';
|
2018-07-10 21:08:22 +00:00
|
|
|
strbuf_addch(sb, c);
|
2015-07-21 21:04:50 +00:00
|
|
|
}
|
2018-07-10 21:08:22 +00:00
|
|
|
strbuf_rtrim(sb);
|
2007-07-29 00:17:17 +00:00
|
|
|
}
|
2007-01-26 22:26:10 +00:00
|
|
|
|
2015-11-10 11:42:36 +00:00
|
|
|
int should_autocreate_reflog(const char *refname)
|
2015-07-21 21:04:51 +00:00
|
|
|
{
|
2017-01-27 10:09:47 +00:00
|
|
|
switch (log_all_ref_updates) {
|
|
|
|
case LOG_REFS_ALWAYS:
|
|
|
|
return 1;
|
|
|
|
case LOG_REFS_NORMAL:
|
|
|
|
return starts_with(refname, "refs/heads/") ||
|
|
|
|
starts_with(refname, "refs/remotes/") ||
|
|
|
|
starts_with(refname, "refs/notes/") ||
|
|
|
|
!strcmp(refname, "HEAD");
|
|
|
|
default:
|
2015-07-21 21:04:51 +00:00
|
|
|
return 0;
|
2017-01-27 10:09:47 +00:00
|
|
|
}
|
2015-07-21 21:04:51 +00:00
|
|
|
}
|
|
|
|
|
2014-07-15 23:02:38 +00:00
|
|
|
int is_branch(const char *refname)
|
2008-01-15 23:50:17 +00:00
|
|
|
{
|
2013-11-30 20:55:40 +00:00
|
|
|
return !strcmp(refname, "HEAD") || starts_with(refname, "refs/heads/");
|
2007-01-26 22:26:09 +00:00
|
|
|
}
|
|
|
|
|
2014-06-03 16:09:59 +00:00
|
|
|
struct read_ref_at_cb {
|
|
|
|
const char *refname;
|
2017-04-26 19:29:31 +00:00
|
|
|
timestamp_t at_time;
|
2014-06-03 16:09:59 +00:00
|
|
|
int cnt;
|
|
|
|
int reccnt;
|
2017-10-15 22:07:03 +00:00
|
|
|
struct object_id *oid;
|
2014-06-03 16:09:59 +00:00
|
|
|
int found_it;
|
|
|
|
|
2017-10-15 22:07:03 +00:00
|
|
|
struct object_id ooid;
|
|
|
|
struct object_id noid;
|
2014-06-03 16:09:59 +00:00
|
|
|
int tz;
|
2017-04-26 19:29:31 +00:00
|
|
|
timestamp_t date;
|
2014-06-03 16:09:59 +00:00
|
|
|
char **msg;
|
2017-04-26 19:29:31 +00:00
|
|
|
timestamp_t *cutoff_time;
|
2014-06-03 16:09:59 +00:00
|
|
|
int *cutoff_tz;
|
|
|
|
int *cutoff_cnt;
|
|
|
|
};
|
|
|
|
|
2017-02-21 23:47:32 +00:00
|
|
|
static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid,
|
2017-04-26 19:29:31 +00:00
|
|
|
const char *email, timestamp_t timestamp, int tz,
|
2014-06-03 16:09:59 +00:00
|
|
|
const char *message, void *cb_data)
|
|
|
|
{
|
|
|
|
struct read_ref_at_cb *cb = cb_data;
|
|
|
|
|
|
|
|
cb->reccnt++;
|
|
|
|
cb->tz = tz;
|
|
|
|
cb->date = timestamp;
|
|
|
|
|
|
|
|
if (timestamp <= cb->at_time || cb->cnt == 0) {
|
|
|
|
if (cb->msg)
|
|
|
|
*cb->msg = xstrdup(message);
|
|
|
|
if (cb->cutoff_time)
|
|
|
|
*cb->cutoff_time = timestamp;
|
|
|
|
if (cb->cutoff_tz)
|
|
|
|
*cb->cutoff_tz = tz;
|
|
|
|
if (cb->cutoff_cnt)
|
|
|
|
*cb->cutoff_cnt = cb->reccnt - 1;
|
|
|
|
/*
|
2017-11-05 08:42:09 +00:00
|
|
|
* we have not yet updated cb->[n|o]oid so they still
|
2014-06-03 16:09:59 +00:00
|
|
|
* hold the values for the previous record.
|
|
|
|
*/
|
2017-10-15 22:07:03 +00:00
|
|
|
if (!is_null_oid(&cb->ooid)) {
|
|
|
|
oidcpy(cb->oid, noid);
|
2018-08-28 21:22:48 +00:00
|
|
|
if (!oideq(&cb->ooid, noid))
|
2018-07-21 07:49:35 +00:00
|
|
|
warning(_("log for ref %s has gap after %s"),
|
convert "enum date_mode" into a struct
In preparation for adding date modes that may carry extra
information beyond the mode itself, this patch converts the
date_mode enum into a struct.
Most of the conversion is fairly straightforward; we pass
the struct as a pointer and dereference the type field where
necessary. Locations that declare a date_mode can use a "{}"
constructor. However, the tricky case is where we use the
enum labels as constants, like:
show_date(t, tz, DATE_NORMAL);
Ideally we could say:
show_date(t, tz, &{ DATE_NORMAL });
but of course C does not allow that. Likewise, we cannot
cast the constant to a struct, because we need to pass an
actual address. Our options are basically:
1. Manually add a "struct date_mode d = { DATE_NORMAL }"
definition to each caller, and pass "&d". This makes
the callers uglier, because they sometimes do not even
have their own scope (e.g., they are inside a switch
statement).
2. Provide a pre-made global "date_normal" struct that can
be passed by address. We'd also need "date_rfc2822",
"date_iso8601", and so forth. But at least the ugliness
is defined in one place.
3. Provide a wrapper that generates the correct struct on
the fly. The big downside is that we end up pointing to
a single global, which makes our wrapper non-reentrant.
But show_date is already not reentrant, so it does not
matter.
This patch implements 3, along with a minor macro to keep
the size of the callers sane.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-06-25 16:55:02 +00:00
|
|
|
cb->refname, show_date(cb->date, cb->tz, DATE_MODE(RFC2822)));
|
2014-06-03 16:09:59 +00:00
|
|
|
}
|
|
|
|
else if (cb->date == cb->at_time)
|
2017-10-15 22:07:03 +00:00
|
|
|
oidcpy(cb->oid, noid);
|
2018-08-28 21:22:48 +00:00
|
|
|
else if (!oideq(noid, cb->oid))
|
2018-07-21 07:49:35 +00:00
|
|
|
warning(_("log for ref %s unexpectedly ended on %s"),
|
2014-06-03 16:09:59 +00:00
|
|
|
cb->refname, show_date(cb->date, cb->tz,
|
convert "enum date_mode" into a struct
In preparation for adding date modes that may carry extra
information beyond the mode itself, this patch converts the
date_mode enum into a struct.
Most of the conversion is fairly straightforward; we pass
the struct as a pointer and dereference the type field where
necessary. Locations that declare a date_mode can use a "{}"
constructor. However, the tricky case is where we use the
enum labels as constants, like:
show_date(t, tz, DATE_NORMAL);
Ideally we could say:
show_date(t, tz, &{ DATE_NORMAL });
but of course C does not allow that. Likewise, we cannot
cast the constant to a struct, because we need to pass an
actual address. Our options are basically:
1. Manually add a "struct date_mode d = { DATE_NORMAL }"
definition to each caller, and pass "&d". This makes
the callers uglier, because they sometimes do not even
have their own scope (e.g., they are inside a switch
statement).
2. Provide a pre-made global "date_normal" struct that can
be passed by address. We'd also need "date_rfc2822",
"date_iso8601", and so forth. But at least the ugliness
is defined in one place.
3. Provide a wrapper that generates the correct struct on
the fly. The big downside is that we end up pointing to
a single global, which makes our wrapper non-reentrant.
But show_date is already not reentrant, so it does not
matter.
This patch implements 3, along with a minor macro to keep
the size of the callers sane.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-06-25 16:55:02 +00:00
|
|
|
DATE_MODE(RFC2822)));
|
2017-10-15 22:07:03 +00:00
|
|
|
oidcpy(&cb->ooid, ooid);
|
|
|
|
oidcpy(&cb->noid, noid);
|
2014-06-03 16:09:59 +00:00
|
|
|
cb->found_it = 1;
|
|
|
|
return 1;
|
|
|
|
}
|
2017-10-15 22:07:03 +00:00
|
|
|
oidcpy(&cb->ooid, ooid);
|
|
|
|
oidcpy(&cb->noid, noid);
|
2014-06-03 16:09:59 +00:00
|
|
|
if (cb->cnt > 0)
|
|
|
|
cb->cnt--;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-02-21 23:47:32 +00:00
|
|
|
static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid,
|
2017-04-26 19:29:31 +00:00
|
|
|
const char *email, timestamp_t timestamp,
|
2014-06-03 16:09:59 +00:00
|
|
|
int tz, const char *message, void *cb_data)
|
|
|
|
{
|
|
|
|
struct read_ref_at_cb *cb = cb_data;
|
|
|
|
|
|
|
|
if (cb->msg)
|
|
|
|
*cb->msg = xstrdup(message);
|
|
|
|
if (cb->cutoff_time)
|
|
|
|
*cb->cutoff_time = timestamp;
|
|
|
|
if (cb->cutoff_tz)
|
|
|
|
*cb->cutoff_tz = tz;
|
|
|
|
if (cb->cutoff_cnt)
|
|
|
|
*cb->cutoff_cnt = cb->reccnt;
|
2017-10-15 22:07:03 +00:00
|
|
|
oidcpy(cb->oid, ooid);
|
|
|
|
if (is_null_oid(cb->oid))
|
|
|
|
oidcpy(cb->oid, noid);
|
2014-06-03 16:09:59 +00:00
|
|
|
/* We just want the first entry */
|
|
|
|
return 1;
|
2007-01-19 09:19:05 +00:00
|
|
|
}
|
|
|
|
|
2017-04-26 19:29:31 +00:00
|
|
|
int read_ref_at(const char *refname, unsigned int flags, timestamp_t at_time, int cnt,
|
2017-10-15 22:07:03 +00:00
|
|
|
struct object_id *oid, char **msg,
|
2017-04-26 19:29:31 +00:00
|
|
|
timestamp_t *cutoff_time, int *cutoff_tz, int *cutoff_cnt)
|
2006-05-17 09:56:09 +00:00
|
|
|
{
|
2014-06-03 16:09:59 +00:00
|
|
|
struct read_ref_at_cb cb;
|
2006-05-17 09:56:09 +00:00
|
|
|
|
2014-06-03 16:09:59 +00:00
|
|
|
memset(&cb, 0, sizeof(cb));
|
|
|
|
cb.refname = refname;
|
|
|
|
cb.at_time = at_time;
|
|
|
|
cb.cnt = cnt;
|
|
|
|
cb.msg = msg;
|
|
|
|
cb.cutoff_time = cutoff_time;
|
|
|
|
cb.cutoff_tz = cutoff_tz;
|
|
|
|
cb.cutoff_cnt = cutoff_cnt;
|
2017-10-15 22:07:03 +00:00
|
|
|
cb.oid = oid;
|
2014-06-03 16:09:59 +00:00
|
|
|
|
|
|
|
for_each_reflog_ent_reverse(refname, read_ref_at_ent, &cb);
|
|
|
|
|
2014-09-19 03:45:37 +00:00
|
|
|
if (!cb.reccnt) {
|
2017-07-13 23:49:29 +00:00
|
|
|
if (flags & GET_OID_QUIETLY)
|
2014-09-19 03:45:37 +00:00
|
|
|
exit(128);
|
|
|
|
else
|
2018-07-21 07:49:35 +00:00
|
|
|
die(_("log for %s is empty"), refname);
|
2014-09-19 03:45:37 +00:00
|
|
|
}
|
2014-06-03 16:09:59 +00:00
|
|
|
if (cb.found_it)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for_each_reflog_ent(refname, read_ref_at_ent_oldest, &cb);
|
2006-05-17 09:56:09 +00:00
|
|
|
|
2007-01-19 09:19:05 +00:00
|
|
|
return 1;
|
2006-05-17 09:56:09 +00:00
|
|
|
}
|
2006-12-18 09:18:16 +00:00
|
|
|
|
2017-03-26 02:42:35 +00:00
|
|
|
struct ref_transaction *ref_store_transaction_begin(struct ref_store *refs,
|
|
|
|
struct strbuf *err)
|
2014-04-07 13:48:10 +00:00
|
|
|
{
|
2017-03-26 02:42:35 +00:00
|
|
|
struct ref_transaction *tr;
|
2014-08-28 23:42:37 +00:00
|
|
|
assert(err);
|
|
|
|
|
2017-03-26 02:42:35 +00:00
|
|
|
tr = xcalloc(1, sizeof(struct ref_transaction));
|
|
|
|
tr->ref_store = refs;
|
|
|
|
return tr;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ref_transaction *ref_transaction_begin(struct strbuf *err)
|
|
|
|
{
|
2018-04-12 00:21:09 +00:00
|
|
|
return ref_store_transaction_begin(get_main_ref_store(the_repository), err);
|
2014-04-07 13:48:10 +00:00
|
|
|
}
|
|
|
|
|
2014-06-20 14:42:42 +00:00
|
|
|
void ref_transaction_free(struct ref_transaction *transaction)
|
2014-04-07 13:48:10 +00:00
|
|
|
{
|
2017-05-22 14:17:37 +00:00
|
|
|
size_t i;
|
2014-04-07 13:48:10 +00:00
|
|
|
|
2014-06-20 14:42:45 +00:00
|
|
|
if (!transaction)
|
|
|
|
return;
|
|
|
|
|
ref_transaction_prepare(): new optional step for reference updates
In the future, compound reference stores will sometimes need to modify
references in two different reference stores at the same time, meaning
that a single logical reference transaction might have to be
implemented as two internal sub-transactions. They won't want to call
`ref_transaction_commit()` for the two sub-transactions one after the
other, because that wouldn't be atomic (the first commit could succeed
and the second one fail). Instead, they will want to prepare both
sub-transactions (i.e., obtain any necessary locks and do any
pre-checks), and only if both prepare steps succeed, then commit both
sub-transactions.
Start preparing for that day by adding a new, optional
`ref_transaction_prepare()` step to the reference transaction
sequence, which obtains the locks and does any prechecks, reporting
any errors that occur. Also add a `ref_transaction_abort()` function
that can be used to abort a sub-transaction even if it has already
been prepared.
That is on the side of the public-facing API. On the side of the
`ref_store` VTABLE, get rid of `transaction_commit` and instead add
methods `transaction_prepare`, `transaction_finish`, and
`transaction_abort`. A `ref_transaction_commit()` now basically calls
methods `transaction_prepare` then `transaction_finish`.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-05-22 14:17:44 +00:00
|
|
|
switch (transaction->state) {
|
|
|
|
case REF_TRANSACTION_OPEN:
|
|
|
|
case REF_TRANSACTION_CLOSED:
|
|
|
|
/* OK */
|
|
|
|
break;
|
|
|
|
case REF_TRANSACTION_PREPARED:
|
2018-05-02 09:38:39 +00:00
|
|
|
BUG("free called on a prepared reference transaction");
|
ref_transaction_prepare(): new optional step for reference updates
In the future, compound reference stores will sometimes need to modify
references in two different reference stores at the same time, meaning
that a single logical reference transaction might have to be
implemented as two internal sub-transactions. They won't want to call
`ref_transaction_commit()` for the two sub-transactions one after the
other, because that wouldn't be atomic (the first commit could succeed
and the second one fail). Instead, they will want to prepare both
sub-transactions (i.e., obtain any necessary locks and do any
pre-checks), and only if both prepare steps succeed, then commit both
sub-transactions.
Start preparing for that day by adding a new, optional
`ref_transaction_prepare()` step to the reference transaction
sequence, which obtains the locks and does any prechecks, reporting
any errors that occur. Also add a `ref_transaction_abort()` function
that can be used to abort a sub-transaction even if it has already
been prepared.
That is on the side of the public-facing API. On the side of the
`ref_store` VTABLE, get rid of `transaction_commit` and instead add
methods `transaction_prepare`, `transaction_finish`, and
`transaction_abort`. A `ref_transaction_commit()` now basically calls
methods `transaction_prepare` then `transaction_finish`.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-05-22 14:17:44 +00:00
|
|
|
break;
|
|
|
|
default:
|
2018-05-02 09:38:39 +00:00
|
|
|
BUG("unexpected reference transaction state");
|
ref_transaction_prepare(): new optional step for reference updates
In the future, compound reference stores will sometimes need to modify
references in two different reference stores at the same time, meaning
that a single logical reference transaction might have to be
implemented as two internal sub-transactions. They won't want to call
`ref_transaction_commit()` for the two sub-transactions one after the
other, because that wouldn't be atomic (the first commit could succeed
and the second one fail). Instead, they will want to prepare both
sub-transactions (i.e., obtain any necessary locks and do any
pre-checks), and only if both prepare steps succeed, then commit both
sub-transactions.
Start preparing for that day by adding a new, optional
`ref_transaction_prepare()` step to the reference transaction
sequence, which obtains the locks and does any prechecks, reporting
any errors that occur. Also add a `ref_transaction_abort()` function
that can be used to abort a sub-transaction even if it has already
been prepared.
That is on the side of the public-facing API. On the side of the
`ref_store` VTABLE, get rid of `transaction_commit` and instead add
methods `transaction_prepare`, `transaction_finish`, and
`transaction_abort`. A `ref_transaction_commit()` now basically calls
methods `transaction_prepare` then `transaction_finish`.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-05-22 14:17:44 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2014-04-30 19:22:42 +00:00
|
|
|
for (i = 0; i < transaction->nr; i++) {
|
|
|
|
free(transaction->updates[i]->msg);
|
2014-04-07 13:48:14 +00:00
|
|
|
free(transaction->updates[i]);
|
2014-04-30 19:22:42 +00:00
|
|
|
}
|
2014-04-07 13:48:10 +00:00
|
|
|
free(transaction->updates);
|
|
|
|
free(transaction);
|
|
|
|
}
|
|
|
|
|
2016-04-25 09:39:54 +00:00
|
|
|
struct ref_update *ref_transaction_add_update(
|
|
|
|
struct ref_transaction *transaction,
|
|
|
|
const char *refname, unsigned int flags,
|
2017-10-15 22:06:53 +00:00
|
|
|
const struct object_id *new_oid,
|
|
|
|
const struct object_id *old_oid,
|
2016-04-25 09:39:54 +00:00
|
|
|
const char *msg)
|
2014-04-07 13:48:10 +00:00
|
|
|
{
|
2016-02-22 22:44:32 +00:00
|
|
|
struct ref_update *update;
|
2016-04-25 09:39:54 +00:00
|
|
|
|
|
|
|
if (transaction->state != REF_TRANSACTION_OPEN)
|
2018-05-02 09:38:39 +00:00
|
|
|
BUG("update called for transaction that is not open");
|
2016-04-25 09:39:54 +00:00
|
|
|
|
2016-02-22 22:44:32 +00:00
|
|
|
FLEX_ALLOC_STR(update, refname, refname);
|
2014-04-07 13:48:10 +00:00
|
|
|
ALLOC_GROW(transaction->updates, transaction->nr + 1, transaction->alloc);
|
|
|
|
transaction->updates[transaction->nr++] = update;
|
2016-04-25 09:39:54 +00:00
|
|
|
|
|
|
|
update->flags = flags;
|
|
|
|
|
|
|
|
if (flags & REF_HAVE_NEW)
|
2017-10-15 22:06:53 +00:00
|
|
|
oidcpy(&update->new_oid, new_oid);
|
2016-04-25 09:39:54 +00:00
|
|
|
if (flags & REF_HAVE_OLD)
|
2017-10-15 22:06:53 +00:00
|
|
|
oidcpy(&update->old_oid, old_oid);
|
2016-10-12 18:20:23 +00:00
|
|
|
update->msg = xstrdup_or_null(msg);
|
2014-04-07 13:48:10 +00:00
|
|
|
return update;
|
|
|
|
}
|
|
|
|
|
2014-06-20 14:43:00 +00:00
|
|
|
int ref_transaction_update(struct ref_transaction *transaction,
|
|
|
|
const char *refname,
|
2017-10-15 22:06:53 +00:00
|
|
|
const struct object_id *new_oid,
|
|
|
|
const struct object_id *old_oid,
|
2015-02-17 17:00:15 +00:00
|
|
|
unsigned int flags, const char *msg,
|
2014-06-20 14:43:00 +00:00
|
|
|
struct strbuf *err)
|
2014-04-07 13:48:10 +00:00
|
|
|
{
|
2014-08-28 23:42:37 +00:00
|
|
|
assert(err);
|
|
|
|
|
2017-10-15 22:06:53 +00:00
|
|
|
if ((new_oid && !is_null_oid(new_oid)) ?
|
2016-04-27 13:54:45 +00:00
|
|
|
check_refname_format(refname, REFNAME_ALLOW_ONELEVEL) :
|
|
|
|
!refname_is_safe(refname)) {
|
2018-07-21 07:49:35 +00:00
|
|
|
strbuf_addf(err, _("refusing to update ref with bad name '%s'"),
|
refs.c: allow listing and deleting badly named refs
We currently do not handle badly named refs well:
$ cp .git/refs/heads/master .git/refs/heads/master.....@\*@\\.
$ git branch
fatal: Reference has invalid format: 'refs/heads/master.....@*@\.'
$ git branch -D master.....@\*@\\.
error: branch 'master.....@*@\.' not found.
Users cannot recover from a badly named ref without manually finding
and deleting the loose ref file or appropriate line in packed-refs.
Making that easier will make it easier to tweak the ref naming rules
in the future, for example to forbid shell metacharacters like '`'
and '"', without putting people in a state that is hard to get out of.
So allow "branch --list" to show these refs and allow "branch -d/-D"
and "update-ref -d" to delete them. Other commands (for example to
rename refs) will continue to not handle these refs but can be changed
in later patches.
Details:
In resolving functions, refuse to resolve refs that don't pass the
git-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 (but not symrefs pointing to badly named refs)
with a REF_BAD_NAME flag to make it easier for future callers to
notice and handle them specially. For example, in a later patch
for-each-ref will use this flag to detect refs whose names can confuse
callers parsing for-each-ref output.
In the transaction API, refuse to create or update badly named refs,
but allow deleting them (unless they try to escape refs/ and don't match
[A-Z_]*).
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-09-03 18:45:43 +00:00
|
|
|
refname);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2017-11-05 08:42:03 +00:00
|
|
|
if (flags & ~REF_TRANSACTION_UPDATE_ALLOWED_FLAGS)
|
|
|
|
BUG("illegal flags 0x%x passed to ref_transaction_update()", flags);
|
refs: strip out not allowed flags from ref_transaction_update
Callers are only allowed to pass certain flags into
ref_transaction_update, other flags are internal to it. To prevent
mistakes from the callers, strip the internal only flags out before
continuing.
This was noticed because of a compiler warning gcc 7.1.1 issued about
passing a NULL parameter as second parameter to memcpy (through
hashcpy):
In file included from refs.c:5:0:
refs.c: In function ‘ref_transaction_verify’:
cache.h:948:2: error: argument 2 null where non-null expected [-Werror=nonnull]
memcpy(sha_dst, sha_src, GIT_SHA1_RAWSZ);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from git-compat-util.h:165:0,
from cache.h:4,
from refs.c:5:
/usr/include/string.h:43:14: note: in a call to function ‘memcpy’ declared here
extern void *memcpy (void *__restrict __dest, const void *__restrict __src,
^~~~~~
The call to hascpy in ref_transaction_add_update is protected by the
passed in flags, but as we only add flags there, gcc notices
REF_HAVE_NEW or REF_HAVE_OLD flags could be passed in from the outside,
which would potentially result in passing in NULL as second parameter to
memcpy.
Fix both the compiler warning, and make the interface safer for its
users by stripping the internal flags out.
Suggested-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-09-12 22:59:21 +00:00
|
|
|
|
2017-10-15 22:06:53 +00:00
|
|
|
flags |= (new_oid ? REF_HAVE_NEW : 0) | (old_oid ? REF_HAVE_OLD : 0);
|
2016-04-25 09:39:54 +00:00
|
|
|
|
|
|
|
ref_transaction_add_update(transaction, refname, flags,
|
2017-10-15 22:06:53 +00:00
|
|
|
new_oid, old_oid, msg);
|
2014-06-20 14:43:00 +00:00
|
|
|
return 0;
|
2014-04-07 13:48:10 +00:00
|
|
|
}
|
|
|
|
|
2014-04-16 22:26:44 +00:00
|
|
|
int ref_transaction_create(struct ref_transaction *transaction,
|
|
|
|
const char *refname,
|
2017-10-15 22:06:53 +00:00
|
|
|
const struct object_id *new_oid,
|
2015-02-17 17:00:13 +00:00
|
|
|
unsigned int flags, const char *msg,
|
2014-04-16 22:26:44 +00:00
|
|
|
struct strbuf *err)
|
2014-04-07 13:48:10 +00:00
|
|
|
{
|
2017-10-15 22:06:53 +00:00
|
|
|
if (!new_oid || is_null_oid(new_oid))
|
2018-05-02 09:38:39 +00:00
|
|
|
BUG("create called without valid new_oid");
|
2017-10-15 22:06:53 +00:00
|
|
|
return ref_transaction_update(transaction, refname, new_oid,
|
|
|
|
&null_oid, flags, msg, err);
|
2014-04-07 13:48:10 +00:00
|
|
|
}
|
|
|
|
|
2014-04-16 22:27:45 +00:00
|
|
|
int ref_transaction_delete(struct ref_transaction *transaction,
|
|
|
|
const char *refname,
|
2017-10-15 22:06:53 +00:00
|
|
|
const struct object_id *old_oid,
|
2015-02-17 17:00:16 +00:00
|
|
|
unsigned int flags, const char *msg,
|
2014-04-16 22:27:45 +00:00
|
|
|
struct strbuf *err)
|
2014-04-07 13:48:10 +00:00
|
|
|
{
|
2017-10-15 22:06:53 +00:00
|
|
|
if (old_oid && is_null_oid(old_oid))
|
2018-05-02 09:38:39 +00:00
|
|
|
BUG("delete called with old_oid set to zeros");
|
2015-02-17 17:00:15 +00:00
|
|
|
return ref_transaction_update(transaction, refname,
|
2017-10-15 22:06:53 +00:00
|
|
|
&null_oid, old_oid,
|
2015-02-17 17:00:15 +00:00
|
|
|
flags, msg, err);
|
2014-04-07 13:48:10 +00:00
|
|
|
}
|
|
|
|
|
2015-02-17 17:00:21 +00:00
|
|
|
int ref_transaction_verify(struct ref_transaction *transaction,
|
|
|
|
const char *refname,
|
2017-10-15 22:06:53 +00:00
|
|
|
const struct object_id *old_oid,
|
2015-02-17 17:00:21 +00:00
|
|
|
unsigned int flags,
|
|
|
|
struct strbuf *err)
|
|
|
|
{
|
2017-10-15 22:06:53 +00:00
|
|
|
if (!old_oid)
|
2018-05-02 09:38:39 +00:00
|
|
|
BUG("verify called with old_oid set to NULL");
|
2015-02-17 17:00:21 +00:00
|
|
|
return ref_transaction_update(transaction, refname,
|
2017-10-15 22:06:53 +00:00
|
|
|
NULL, old_oid,
|
2015-02-17 17:00:21 +00:00
|
|
|
flags, NULL, err);
|
|
|
|
}
|
|
|
|
|
2017-03-26 02:42:35 +00:00
|
|
|
int refs_update_ref(struct ref_store *refs, const char *msg,
|
2017-10-15 22:06:51 +00:00
|
|
|
const char *refname, const struct object_id *new_oid,
|
|
|
|
const struct object_id *old_oid, unsigned int flags,
|
2017-03-26 02:42:35 +00:00
|
|
|
enum action_on_err onerr)
|
2013-09-04 15:22:40 +00:00
|
|
|
{
|
2015-07-31 06:06:19 +00:00
|
|
|
struct ref_transaction *t = NULL;
|
2014-04-24 23:36:55 +00:00
|
|
|
struct strbuf err = STRBUF_INIT;
|
2015-07-31 06:06:19 +00:00
|
|
|
int ret = 0;
|
2014-04-24 23:36:55 +00:00
|
|
|
|
2015-07-31 06:06:19 +00:00
|
|
|
if (ref_type(refname) == REF_TYPE_PSEUDOREF) {
|
2018-04-12 00:21:09 +00:00
|
|
|
assert(refs == get_main_ref_store(the_repository));
|
2017-10-15 22:06:51 +00:00
|
|
|
ret = write_pseudoref(refname, new_oid, old_oid, &err);
|
2015-07-31 06:06:19 +00:00
|
|
|
} else {
|
2017-03-26 02:42:35 +00:00
|
|
|
t = ref_store_transaction_begin(refs, &err);
|
2015-07-31 06:06:19 +00:00
|
|
|
if (!t ||
|
2017-10-15 22:06:53 +00:00
|
|
|
ref_transaction_update(t, refname, new_oid, old_oid,
|
2015-07-31 06:06:19 +00:00
|
|
|
flags, msg, &err) ||
|
|
|
|
ref_transaction_commit(t, &err)) {
|
|
|
|
ret = 1;
|
|
|
|
ref_transaction_free(t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ret) {
|
2018-07-21 07:49:35 +00:00
|
|
|
const char *str = _("update_ref failed for ref '%s': %s");
|
2014-04-24 23:36:55 +00:00
|
|
|
|
|
|
|
switch (onerr) {
|
|
|
|
case UPDATE_REFS_MSG_ON_ERR:
|
|
|
|
error(str, refname, err.buf);
|
|
|
|
break;
|
|
|
|
case UPDATE_REFS_DIE_ON_ERR:
|
|
|
|
die(str, refname, err.buf);
|
|
|
|
break;
|
|
|
|
case UPDATE_REFS_QUIET_ON_ERR:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
strbuf_release(&err);
|
2013-09-04 15:22:40 +00:00
|
|
|
return 1;
|
2014-04-24 23:36:55 +00:00
|
|
|
}
|
|
|
|
strbuf_release(&err);
|
2015-07-31 06:06:19 +00:00
|
|
|
if (t)
|
|
|
|
ref_transaction_free(t);
|
2014-04-24 23:36:55 +00:00
|
|
|
return 0;
|
2013-09-04 15:22:40 +00:00
|
|
|
}
|
|
|
|
|
2017-03-26 02:42:35 +00:00
|
|
|
int update_ref(const char *msg, const char *refname,
|
2017-10-15 22:06:51 +00:00
|
|
|
const struct object_id *new_oid,
|
|
|
|
const struct object_id *old_oid,
|
2017-03-26 02:42:35 +00:00
|
|
|
unsigned int flags, enum action_on_err onerr)
|
|
|
|
{
|
2018-04-12 00:21:09 +00:00
|
|
|
return refs_update_ref(get_main_ref_store(the_repository), msg, refname, new_oid,
|
2017-10-15 22:06:51 +00:00
|
|
|
old_oid, flags, onerr);
|
2017-03-26 02:42:35 +00:00
|
|
|
}
|
|
|
|
|
2011-12-12 05:38:09 +00:00
|
|
|
char *shorten_unambiguous_ref(const char *refname, int strict)
|
2009-04-07 07:14:20 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
static char **scanf_fmts;
|
|
|
|
static int nr_rules;
|
|
|
|
char *short_name;
|
2017-03-28 19:46:33 +00:00
|
|
|
struct strbuf resolved_buf = STRBUF_INIT;
|
2009-04-07 07:14:20 +00:00
|
|
|
|
|
|
|
if (!nr_rules) {
|
2014-01-08 14:43:39 +00:00
|
|
|
/*
|
|
|
|
* Pre-generate scanf formats from ref_rev_parse_rules[].
|
|
|
|
* Generate a format suitable for scanf from a
|
|
|
|
* ref_rev_parse_rules rule by interpolating "%s" at the
|
|
|
|
* location of the "%.*s".
|
|
|
|
*/
|
2009-04-07 07:14:20 +00:00
|
|
|
size_t total_len = 0;
|
2014-01-08 14:43:38 +00:00
|
|
|
size_t offset = 0;
|
2009-04-07 07:14:20 +00:00
|
|
|
|
|
|
|
/* the rule list is NULL terminated, count them first */
|
2013-10-24 08:45:13 +00:00
|
|
|
for (nr_rules = 0; ref_rev_parse_rules[nr_rules]; nr_rules++)
|
2014-01-08 14:43:40 +00:00
|
|
|
/* -2 for strlen("%.*s") - strlen("%s"); +1 for NUL */
|
|
|
|
total_len += strlen(ref_rev_parse_rules[nr_rules]) - 2 + 1;
|
2009-04-07 07:14:20 +00:00
|
|
|
|
2016-07-30 18:18:31 +00:00
|
|
|
scanf_fmts = xmalloc(st_add(st_mult(sizeof(char *), nr_rules), total_len));
|
2009-04-07 07:14:20 +00:00
|
|
|
|
2014-01-08 14:43:38 +00:00
|
|
|
offset = 0;
|
2009-04-07 07:14:20 +00:00
|
|
|
for (i = 0; i < nr_rules; i++) {
|
2014-01-08 14:43:39 +00:00
|
|
|
assert(offset < total_len);
|
2014-01-08 14:43:38 +00:00
|
|
|
scanf_fmts[i] = (char *)&scanf_fmts[nr_rules] + offset;
|
2018-05-19 01:58:20 +00:00
|
|
|
offset += xsnprintf(scanf_fmts[i], total_len - offset,
|
|
|
|
ref_rev_parse_rules[i], 2, "%s") + 1;
|
2009-04-07 07:14:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* bail out if there are no rules */
|
|
|
|
if (!nr_rules)
|
2011-12-12 05:38:09 +00:00
|
|
|
return xstrdup(refname);
|
2009-04-07 07:14:20 +00:00
|
|
|
|
2011-12-12 05:38:09 +00:00
|
|
|
/* buffer for scanf result, at most refname must fit */
|
|
|
|
short_name = xstrdup(refname);
|
2009-04-07 07:14:20 +00:00
|
|
|
|
|
|
|
/* skip first rule, it will always match */
|
|
|
|
for (i = nr_rules - 1; i > 0 ; --i) {
|
|
|
|
int j;
|
2009-04-13 10:25:46 +00:00
|
|
|
int rules_to_fail = i;
|
2009-04-07 07:14:20 +00:00
|
|
|
int short_name_len;
|
|
|
|
|
2011-12-12 05:38:09 +00:00
|
|
|
if (1 != sscanf(refname, scanf_fmts[i], short_name))
|
2009-04-07 07:14:20 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
short_name_len = strlen(short_name);
|
|
|
|
|
2009-04-13 10:25:46 +00:00
|
|
|
/*
|
|
|
|
* in strict mode, all (except the matched one) rules
|
|
|
|
* must fail to resolve to a valid non-ambiguous ref
|
|
|
|
*/
|
|
|
|
if (strict)
|
|
|
|
rules_to_fail = nr_rules;
|
|
|
|
|
2009-04-07 07:14:20 +00:00
|
|
|
/*
|
|
|
|
* check if the short name resolves to a valid ref,
|
|
|
|
* but use only rules prior to the matched one
|
|
|
|
*/
|
2009-04-13 10:25:46 +00:00
|
|
|
for (j = 0; j < rules_to_fail; j++) {
|
2009-04-07 07:14:20 +00:00
|
|
|
const char *rule = ref_rev_parse_rules[j];
|
|
|
|
|
2009-04-13 10:25:46 +00:00
|
|
|
/* skip matched rule */
|
|
|
|
if (i == j)
|
|
|
|
continue;
|
|
|
|
|
2009-04-07 07:14:20 +00:00
|
|
|
/*
|
|
|
|
* the short name is ambiguous, if it resolves
|
|
|
|
* (with this previous rule) to a valid ref
|
|
|
|
* read_ref() returns 0 on success
|
|
|
|
*/
|
2017-03-28 19:46:33 +00:00
|
|
|
strbuf_reset(&resolved_buf);
|
|
|
|
strbuf_addf(&resolved_buf, rule,
|
|
|
|
short_name_len, short_name);
|
|
|
|
if (ref_exists(resolved_buf.buf))
|
2009-04-07 07:14:20 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* short name is non-ambiguous if all previous rules
|
|
|
|
* haven't resolved to a valid ref
|
|
|
|
*/
|
2017-03-28 19:46:33 +00:00
|
|
|
if (j == rules_to_fail) {
|
|
|
|
strbuf_release(&resolved_buf);
|
2009-04-07 07:14:20 +00:00
|
|
|
return short_name;
|
2017-03-28 19:46:33 +00:00
|
|
|
}
|
2009-04-07 07:14:20 +00:00
|
|
|
}
|
|
|
|
|
2017-03-28 19:46:33 +00:00
|
|
|
strbuf_release(&resolved_buf);
|
2009-04-07 07:14:20 +00:00
|
|
|
free(short_name);
|
2011-12-12 05:38:09 +00:00
|
|
|
return xstrdup(refname);
|
2009-04-07 07:14:20 +00:00
|
|
|
}
|
upload/receive-pack: allow hiding ref hierarchies
A repository may have refs that are only used for its internal
bookkeeping purposes that should not be exposed to the others that
come over the network.
Teach upload-pack to omit some refs from its initial advertisement
by paying attention to the uploadpack.hiderefs multi-valued
configuration variable. Do the same to receive-pack via the
receive.hiderefs variable. As a convenient short-hand, allow using
transfer.hiderefs to set the value to both of these variables.
Any ref that is under the hierarchies listed on the value of these
variable is excluded from responses to requests made by "ls-remote",
"fetch", etc. (for upload-pack) and "push" (for receive-pack).
Because these hidden refs do not count as OUR_REF, an attempt to
fetch objects at the tip of them will be rejected, and because these
refs do not get advertised, "git push :" will not see local branches
that have the same name as them as "matching" ones to be sent.
An attempt to update/delete these hidden refs with an explicit
refspec, e.g. "git push origin :refs/hidden/22", is rejected. This
is not a new restriction. To the pusher, it would appear that there
is no such ref, so its push request will conclude with "Now that I
sent you all the data, it is time for you to update the refs. I saw
that the ref did not exist when I started pushing, and I want the
result to point at this commit". The receiving end will apply the
compare-and-swap rule to this request and rejects the push with
"Well, your update request conflicts with somebody else; I see there
is such a ref.", which is the right thing to do. Otherwise a push to
a hidden ref will always be "the last one wins", which is not a good
default.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-01-19 00:08:30 +00:00
|
|
|
|
|
|
|
static struct string_list *hide_refs;
|
|
|
|
|
|
|
|
int parse_hide_refs_config(const char *var, const char *value, const char *section)
|
|
|
|
{
|
2017-02-24 21:08:16 +00:00
|
|
|
const char *key;
|
upload/receive-pack: allow hiding ref hierarchies
A repository may have refs that are only used for its internal
bookkeeping purposes that should not be exposed to the others that
come over the network.
Teach upload-pack to omit some refs from its initial advertisement
by paying attention to the uploadpack.hiderefs multi-valued
configuration variable. Do the same to receive-pack via the
receive.hiderefs variable. As a convenient short-hand, allow using
transfer.hiderefs to set the value to both of these variables.
Any ref that is under the hierarchies listed on the value of these
variable is excluded from responses to requests made by "ls-remote",
"fetch", etc. (for upload-pack) and "push" (for receive-pack).
Because these hidden refs do not count as OUR_REF, an attempt to
fetch objects at the tip of them will be rejected, and because these
refs do not get advertised, "git push :" will not see local branches
that have the same name as them as "matching" ones to be sent.
An attempt to update/delete these hidden refs with an explicit
refspec, e.g. "git push origin :refs/hidden/22", is rejected. This
is not a new restriction. To the pusher, it would appear that there
is no such ref, so its push request will conclude with "Now that I
sent you all the data, it is time for you to update the refs. I saw
that the ref did not exist when I started pushing, and I want the
result to point at this commit". The receiving end will apply the
compare-and-swap rule to this request and rejects the push with
"Well, your update request conflicts with somebody else; I see there
is such a ref.", which is the right thing to do. Otherwise a push to
a hidden ref will always be "the last one wins", which is not a good
default.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-01-19 00:08:30 +00:00
|
|
|
if (!strcmp("transfer.hiderefs", var) ||
|
2017-02-24 21:08:16 +00:00
|
|
|
(!parse_config_key(var, section, NULL, NULL, &key) &&
|
|
|
|
!strcmp(key, "hiderefs"))) {
|
upload/receive-pack: allow hiding ref hierarchies
A repository may have refs that are only used for its internal
bookkeeping purposes that should not be exposed to the others that
come over the network.
Teach upload-pack to omit some refs from its initial advertisement
by paying attention to the uploadpack.hiderefs multi-valued
configuration variable. Do the same to receive-pack via the
receive.hiderefs variable. As a convenient short-hand, allow using
transfer.hiderefs to set the value to both of these variables.
Any ref that is under the hierarchies listed on the value of these
variable is excluded from responses to requests made by "ls-remote",
"fetch", etc. (for upload-pack) and "push" (for receive-pack).
Because these hidden refs do not count as OUR_REF, an attempt to
fetch objects at the tip of them will be rejected, and because these
refs do not get advertised, "git push :" will not see local branches
that have the same name as them as "matching" ones to be sent.
An attempt to update/delete these hidden refs with an explicit
refspec, e.g. "git push origin :refs/hidden/22", is rejected. This
is not a new restriction. To the pusher, it would appear that there
is no such ref, so its push request will conclude with "Now that I
sent you all the data, it is time for you to update the refs. I saw
that the ref did not exist when I started pushing, and I want the
result to point at this commit". The receiving end will apply the
compare-and-swap rule to this request and rejects the push with
"Well, your update request conflicts with somebody else; I see there
is such a ref.", which is the right thing to do. Otherwise a push to
a hidden ref will always be "the last one wins", which is not a good
default.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-01-19 00:08:30 +00:00
|
|
|
char *ref;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
if (!value)
|
|
|
|
return config_error_nonbool(var);
|
|
|
|
ref = xstrdup(value);
|
|
|
|
len = strlen(ref);
|
|
|
|
while (len && ref[len - 1] == '/')
|
|
|
|
ref[--len] = '\0';
|
|
|
|
if (!hide_refs) {
|
|
|
|
hide_refs = xcalloc(1, sizeof(*hide_refs));
|
|
|
|
hide_refs->strdup_strings = 1;
|
|
|
|
}
|
|
|
|
string_list_append(hide_refs, ref);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-11-03 07:58:16 +00:00
|
|
|
int ref_is_hidden(const char *refname, const char *refname_full)
|
upload/receive-pack: allow hiding ref hierarchies
A repository may have refs that are only used for its internal
bookkeeping purposes that should not be exposed to the others that
come over the network.
Teach upload-pack to omit some refs from its initial advertisement
by paying attention to the uploadpack.hiderefs multi-valued
configuration variable. Do the same to receive-pack via the
receive.hiderefs variable. As a convenient short-hand, allow using
transfer.hiderefs to set the value to both of these variables.
Any ref that is under the hierarchies listed on the value of these
variable is excluded from responses to requests made by "ls-remote",
"fetch", etc. (for upload-pack) and "push" (for receive-pack).
Because these hidden refs do not count as OUR_REF, an attempt to
fetch objects at the tip of them will be rejected, and because these
refs do not get advertised, "git push :" will not see local branches
that have the same name as them as "matching" ones to be sent.
An attempt to update/delete these hidden refs with an explicit
refspec, e.g. "git push origin :refs/hidden/22", is rejected. This
is not a new restriction. To the pusher, it would appear that there
is no such ref, so its push request will conclude with "Now that I
sent you all the data, it is time for you to update the refs. I saw
that the ref did not exist when I started pushing, and I want the
result to point at this commit". The receiving end will apply the
compare-and-swap rule to this request and rejects the push with
"Well, your update request conflicts with somebody else; I see there
is such a ref.", which is the right thing to do. Otherwise a push to
a hidden ref will always be "the last one wins", which is not a good
default.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-01-19 00:08:30 +00:00
|
|
|
{
|
refs: support negative transfer.hideRefs
If you hide a hierarchy of refs using the transfer.hideRefs
config, there is no way to later override that config to
"unhide" it. This patch implements a "negative" hide which
causes matches to immediately be marked as unhidden, even if
another match would hide it. We take care to apply the
matches in reverse-order from how they are fed to us by the
config machinery, as that lets our usual "last one wins"
config precedence work (and entries in .git/config, for
example, will override /etc/gitconfig).
So you can now do:
$ git config --system transfer.hideRefs refs/secret
$ git config transfer.hideRefs '!refs/secret/not-so-secret'
to hide refs/secret in all repos, except for one public bit
in one specific repo. Or you can even do:
$ git clone \
-u "git -c transfer.hiderefs="!refs/foo" upload-pack" \
remote:repo.git
to clone remote:repo.git, overriding any hiding it has
configured.
There are two alternatives that were considered and
rejected:
1. A generic config mechanism for removing an item from a
list. E.g.: (e.g., "[transfer] hideRefs -= refs/foo").
This is nice because it could apply to other
multi-valued config, as well. But it is not nearly as
flexible. There is no way to say:
[transfer]
hideRefs = refs/secret
hideRefs = refs/secret/not-so-secret
Having explicit negative specifications means we can
override previous entries, even if they are not the
same literal string.
2. Adding another variable to override some parts of
hideRefs (e.g., "exposeRefs").
This solves the problem from alternative (1), but it
cannot easily obey the normal config precedence,
because it would use two separate lists. For example:
[transfer]
hideRefs = refs/secret
exposeRefs = refs/secret/not-so-secret
hideRefs = refs/secret/not-so-secret/no-really-its-secret
With two lists, we have to apply the "expose" rules
first, and only then apply the "hide" rules. But that
does not match what the above config intends.
Of course we could internally parse that to a single
list, respecting the ordering, which saves us having to
invent the new "!" syntax. But using a single name
communicates to the user that the ordering _is_
important. And "!" is well-known for negation, and
should not appear at the beginning of a ref (it is
actually valid in a ref-name, but all entries here
should be fully-qualified, starting with "refs/").
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-07-28 20:23:26 +00:00
|
|
|
int i;
|
upload/receive-pack: allow hiding ref hierarchies
A repository may have refs that are only used for its internal
bookkeeping purposes that should not be exposed to the others that
come over the network.
Teach upload-pack to omit some refs from its initial advertisement
by paying attention to the uploadpack.hiderefs multi-valued
configuration variable. Do the same to receive-pack via the
receive.hiderefs variable. As a convenient short-hand, allow using
transfer.hiderefs to set the value to both of these variables.
Any ref that is under the hierarchies listed on the value of these
variable is excluded from responses to requests made by "ls-remote",
"fetch", etc. (for upload-pack) and "push" (for receive-pack).
Because these hidden refs do not count as OUR_REF, an attempt to
fetch objects at the tip of them will be rejected, and because these
refs do not get advertised, "git push :" will not see local branches
that have the same name as them as "matching" ones to be sent.
An attempt to update/delete these hidden refs with an explicit
refspec, e.g. "git push origin :refs/hidden/22", is rejected. This
is not a new restriction. To the pusher, it would appear that there
is no such ref, so its push request will conclude with "Now that I
sent you all the data, it is time for you to update the refs. I saw
that the ref did not exist when I started pushing, and I want the
result to point at this commit". The receiving end will apply the
compare-and-swap rule to this request and rejects the push with
"Well, your update request conflicts with somebody else; I see there
is such a ref.", which is the right thing to do. Otherwise a push to
a hidden ref will always be "the last one wins", which is not a good
default.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-01-19 00:08:30 +00:00
|
|
|
|
|
|
|
if (!hide_refs)
|
|
|
|
return 0;
|
refs: support negative transfer.hideRefs
If you hide a hierarchy of refs using the transfer.hideRefs
config, there is no way to later override that config to
"unhide" it. This patch implements a "negative" hide which
causes matches to immediately be marked as unhidden, even if
another match would hide it. We take care to apply the
matches in reverse-order from how they are fed to us by the
config machinery, as that lets our usual "last one wins"
config precedence work (and entries in .git/config, for
example, will override /etc/gitconfig).
So you can now do:
$ git config --system transfer.hideRefs refs/secret
$ git config transfer.hideRefs '!refs/secret/not-so-secret'
to hide refs/secret in all repos, except for one public bit
in one specific repo. Or you can even do:
$ git clone \
-u "git -c transfer.hiderefs="!refs/foo" upload-pack" \
remote:repo.git
to clone remote:repo.git, overriding any hiding it has
configured.
There are two alternatives that were considered and
rejected:
1. A generic config mechanism for removing an item from a
list. E.g.: (e.g., "[transfer] hideRefs -= refs/foo").
This is nice because it could apply to other
multi-valued config, as well. But it is not nearly as
flexible. There is no way to say:
[transfer]
hideRefs = refs/secret
hideRefs = refs/secret/not-so-secret
Having explicit negative specifications means we can
override previous entries, even if they are not the
same literal string.
2. Adding another variable to override some parts of
hideRefs (e.g., "exposeRefs").
This solves the problem from alternative (1), but it
cannot easily obey the normal config precedence,
because it would use two separate lists. For example:
[transfer]
hideRefs = refs/secret
exposeRefs = refs/secret/not-so-secret
hideRefs = refs/secret/not-so-secret/no-really-its-secret
With two lists, we have to apply the "expose" rules
first, and only then apply the "hide" rules. But that
does not match what the above config intends.
Of course we could internally parse that to a single
list, respecting the ordering, which saves us having to
invent the new "!" syntax. But using a single name
communicates to the user that the ordering _is_
important. And "!" is well-known for negation, and
should not appear at the beginning of a ref (it is
actually valid in a ref-name, but all entries here
should be fully-qualified, starting with "refs/").
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-07-28 20:23:26 +00:00
|
|
|
for (i = hide_refs->nr - 1; i >= 0; i--) {
|
|
|
|
const char *match = hide_refs->items[i].string;
|
2015-11-03 07:58:16 +00:00
|
|
|
const char *subject;
|
refs: support negative transfer.hideRefs
If you hide a hierarchy of refs using the transfer.hideRefs
config, there is no way to later override that config to
"unhide" it. This patch implements a "negative" hide which
causes matches to immediately be marked as unhidden, even if
another match would hide it. We take care to apply the
matches in reverse-order from how they are fed to us by the
config machinery, as that lets our usual "last one wins"
config precedence work (and entries in .git/config, for
example, will override /etc/gitconfig).
So you can now do:
$ git config --system transfer.hideRefs refs/secret
$ git config transfer.hideRefs '!refs/secret/not-so-secret'
to hide refs/secret in all repos, except for one public bit
in one specific repo. Or you can even do:
$ git clone \
-u "git -c transfer.hiderefs="!refs/foo" upload-pack" \
remote:repo.git
to clone remote:repo.git, overriding any hiding it has
configured.
There are two alternatives that were considered and
rejected:
1. A generic config mechanism for removing an item from a
list. E.g.: (e.g., "[transfer] hideRefs -= refs/foo").
This is nice because it could apply to other
multi-valued config, as well. But it is not nearly as
flexible. There is no way to say:
[transfer]
hideRefs = refs/secret
hideRefs = refs/secret/not-so-secret
Having explicit negative specifications means we can
override previous entries, even if they are not the
same literal string.
2. Adding another variable to override some parts of
hideRefs (e.g., "exposeRefs").
This solves the problem from alternative (1), but it
cannot easily obey the normal config precedence,
because it would use two separate lists. For example:
[transfer]
hideRefs = refs/secret
exposeRefs = refs/secret/not-so-secret
hideRefs = refs/secret/not-so-secret/no-really-its-secret
With two lists, we have to apply the "expose" rules
first, and only then apply the "hide" rules. But that
does not match what the above config intends.
Of course we could internally parse that to a single
list, respecting the ordering, which saves us having to
invent the new "!" syntax. But using a single name
communicates to the user that the ordering _is_
important. And "!" is well-known for negation, and
should not appear at the beginning of a ref (it is
actually valid in a ref-name, but all entries here
should be fully-qualified, starting with "refs/").
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-07-28 20:23:26 +00:00
|
|
|
int neg = 0;
|
2017-07-22 04:39:12 +00:00
|
|
|
const char *p;
|
refs: support negative transfer.hideRefs
If you hide a hierarchy of refs using the transfer.hideRefs
config, there is no way to later override that config to
"unhide" it. This patch implements a "negative" hide which
causes matches to immediately be marked as unhidden, even if
another match would hide it. We take care to apply the
matches in reverse-order from how they are fed to us by the
config machinery, as that lets our usual "last one wins"
config precedence work (and entries in .git/config, for
example, will override /etc/gitconfig).
So you can now do:
$ git config --system transfer.hideRefs refs/secret
$ git config transfer.hideRefs '!refs/secret/not-so-secret'
to hide refs/secret in all repos, except for one public bit
in one specific repo. Or you can even do:
$ git clone \
-u "git -c transfer.hiderefs="!refs/foo" upload-pack" \
remote:repo.git
to clone remote:repo.git, overriding any hiding it has
configured.
There are two alternatives that were considered and
rejected:
1. A generic config mechanism for removing an item from a
list. E.g.: (e.g., "[transfer] hideRefs -= refs/foo").
This is nice because it could apply to other
multi-valued config, as well. But it is not nearly as
flexible. There is no way to say:
[transfer]
hideRefs = refs/secret
hideRefs = refs/secret/not-so-secret
Having explicit negative specifications means we can
override previous entries, even if they are not the
same literal string.
2. Adding another variable to override some parts of
hideRefs (e.g., "exposeRefs").
This solves the problem from alternative (1), but it
cannot easily obey the normal config precedence,
because it would use two separate lists. For example:
[transfer]
hideRefs = refs/secret
exposeRefs = refs/secret/not-so-secret
hideRefs = refs/secret/not-so-secret/no-really-its-secret
With two lists, we have to apply the "expose" rules
first, and only then apply the "hide" rules. But that
does not match what the above config intends.
Of course we could internally parse that to a single
list, respecting the ordering, which saves us having to
invent the new "!" syntax. But using a single name
communicates to the user that the ordering _is_
important. And "!" is well-known for negation, and
should not appear at the beginning of a ref (it is
actually valid in a ref-name, but all entries here
should be fully-qualified, starting with "refs/").
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-07-28 20:23:26 +00:00
|
|
|
|
|
|
|
if (*match == '!') {
|
|
|
|
neg = 1;
|
|
|
|
match++;
|
|
|
|
}
|
|
|
|
|
2015-11-03 07:58:16 +00:00
|
|
|
if (*match == '^') {
|
|
|
|
subject = refname_full;
|
|
|
|
match++;
|
|
|
|
} else {
|
|
|
|
subject = refname;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* refname can be NULL when namespaces are used. */
|
2017-07-22 04:39:12 +00:00
|
|
|
if (subject &&
|
|
|
|
skip_prefix(subject, match, &p) &&
|
|
|
|
(!*p || *p == '/'))
|
refs: support negative transfer.hideRefs
If you hide a hierarchy of refs using the transfer.hideRefs
config, there is no way to later override that config to
"unhide" it. This patch implements a "negative" hide which
causes matches to immediately be marked as unhidden, even if
another match would hide it. We take care to apply the
matches in reverse-order from how they are fed to us by the
config machinery, as that lets our usual "last one wins"
config precedence work (and entries in .git/config, for
example, will override /etc/gitconfig).
So you can now do:
$ git config --system transfer.hideRefs refs/secret
$ git config transfer.hideRefs '!refs/secret/not-so-secret'
to hide refs/secret in all repos, except for one public bit
in one specific repo. Or you can even do:
$ git clone \
-u "git -c transfer.hiderefs="!refs/foo" upload-pack" \
remote:repo.git
to clone remote:repo.git, overriding any hiding it has
configured.
There are two alternatives that were considered and
rejected:
1. A generic config mechanism for removing an item from a
list. E.g.: (e.g., "[transfer] hideRefs -= refs/foo").
This is nice because it could apply to other
multi-valued config, as well. But it is not nearly as
flexible. There is no way to say:
[transfer]
hideRefs = refs/secret
hideRefs = refs/secret/not-so-secret
Having explicit negative specifications means we can
override previous entries, even if they are not the
same literal string.
2. Adding another variable to override some parts of
hideRefs (e.g., "exposeRefs").
This solves the problem from alternative (1), but it
cannot easily obey the normal config precedence,
because it would use two separate lists. For example:
[transfer]
hideRefs = refs/secret
exposeRefs = refs/secret/not-so-secret
hideRefs = refs/secret/not-so-secret/no-really-its-secret
With two lists, we have to apply the "expose" rules
first, and only then apply the "hide" rules. But that
does not match what the above config intends.
Of course we could internally parse that to a single
list, respecting the ordering, which saves us having to
invent the new "!" syntax. But using a single name
communicates to the user that the ordering _is_
important. And "!" is well-known for negation, and
should not appear at the beginning of a ref (it is
actually valid in a ref-name, but all entries here
should be fully-qualified, starting with "refs/").
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-07-28 20:23:26 +00:00
|
|
|
return !neg;
|
upload/receive-pack: allow hiding ref hierarchies
A repository may have refs that are only used for its internal
bookkeeping purposes that should not be exposed to the others that
come over the network.
Teach upload-pack to omit some refs from its initial advertisement
by paying attention to the uploadpack.hiderefs multi-valued
configuration variable. Do the same to receive-pack via the
receive.hiderefs variable. As a convenient short-hand, allow using
transfer.hiderefs to set the value to both of these variables.
Any ref that is under the hierarchies listed on the value of these
variable is excluded from responses to requests made by "ls-remote",
"fetch", etc. (for upload-pack) and "push" (for receive-pack).
Because these hidden refs do not count as OUR_REF, an attempt to
fetch objects at the tip of them will be rejected, and because these
refs do not get advertised, "git push :" will not see local branches
that have the same name as them as "matching" ones to be sent.
An attempt to update/delete these hidden refs with an explicit
refspec, e.g. "git push origin :refs/hidden/22", is rejected. This
is not a new restriction. To the pusher, it would appear that there
is no such ref, so its push request will conclude with "Now that I
sent you all the data, it is time for you to update the refs. I saw
that the ref did not exist when I started pushing, and I want the
result to point at this commit". The receiving end will apply the
compare-and-swap rule to this request and rejects the push with
"Well, your update request conflicts with somebody else; I see there
is such a ref.", which is the right thing to do. Otherwise a push to
a hidden ref will always be "the last one wins", which is not a good
default.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-01-19 00:08:30 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2014-12-12 08:56:59 +00:00
|
|
|
|
2015-11-10 11:42:40 +00:00
|
|
|
const char *find_descendant_ref(const char *dirname,
|
|
|
|
const struct string_list *extras,
|
|
|
|
const struct string_list *skip)
|
2014-12-12 08:56:59 +00:00
|
|
|
{
|
2015-11-10 11:42:40 +00:00
|
|
|
int pos;
|
2014-12-12 08:56:59 +00:00
|
|
|
|
2015-11-10 11:42:40 +00:00
|
|
|
if (!extras)
|
|
|
|
return NULL;
|
2014-12-12 08:56:59 +00:00
|
|
|
|
|
|
|
/*
|
2015-11-10 11:42:40 +00:00
|
|
|
* Look at the place where dirname would be inserted into
|
|
|
|
* extras. If there is an entry at that position that starts
|
|
|
|
* with dirname (remember, dirname includes the trailing
|
|
|
|
* slash) and is not in skip, then we have a conflict.
|
2014-12-12 08:56:59 +00:00
|
|
|
*/
|
2015-11-10 11:42:40 +00:00
|
|
|
for (pos = string_list_find_insert_index(extras, dirname, 0);
|
|
|
|
pos < extras->nr; pos++) {
|
|
|
|
const char *extra_refname = extras->items[pos].string;
|
2014-12-12 08:56:59 +00:00
|
|
|
|
2015-11-10 11:42:40 +00:00
|
|
|
if (!starts_with(extra_refname, dirname))
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (!skip || !string_list_has_string(skip, extra_refname))
|
|
|
|
return extra_refname;
|
2014-12-12 08:56:59 +00:00
|
|
|
}
|
2015-11-10 11:42:40 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2014-12-12 08:56:59 +00:00
|
|
|
|
2017-03-26 02:42:34 +00:00
|
|
|
int refs_rename_ref_available(struct ref_store *refs,
|
|
|
|
const char *old_refname,
|
|
|
|
const char *new_refname)
|
2015-11-10 11:42:40 +00:00
|
|
|
{
|
|
|
|
struct string_list skip = STRING_LIST_INIT_NODUP;
|
|
|
|
struct strbuf err = STRBUF_INIT;
|
2016-09-04 16:08:08 +00:00
|
|
|
int ok;
|
2014-12-12 08:56:59 +00:00
|
|
|
|
2016-09-04 16:08:08 +00:00
|
|
|
string_list_insert(&skip, old_refname);
|
2017-03-26 02:42:34 +00:00
|
|
|
ok = !refs_verify_refname_available(refs, new_refname,
|
|
|
|
NULL, &skip, &err);
|
2016-09-04 16:08:08 +00:00
|
|
|
if (!ok)
|
2015-11-10 11:42:40 +00:00
|
|
|
error("%s", err.buf);
|
|
|
|
|
|
|
|
string_list_clear(&skip, 0);
|
|
|
|
strbuf_release(&err);
|
2016-09-04 16:08:08 +00:00
|
|
|
return ok;
|
2014-12-12 08:56:59 +00:00
|
|
|
}
|
2016-04-07 19:02:48 +00:00
|
|
|
|
2017-08-23 12:36:55 +00:00
|
|
|
int refs_head_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
|
2016-04-07 19:02:48 +00:00
|
|
|
{
|
|
|
|
struct object_id oid;
|
|
|
|
int flag;
|
|
|
|
|
2017-08-23 12:36:55 +00:00
|
|
|
if (!refs_read_ref_full(refs, "HEAD", RESOLVE_REF_READING,
|
2017-10-15 22:06:56 +00:00
|
|
|
&oid, &flag))
|
2016-04-07 19:02:48 +00:00
|
|
|
return fn("HEAD", &oid, flag, cb_data);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int head_ref(each_ref_fn fn, void *cb_data)
|
|
|
|
{
|
2018-04-12 00:21:09 +00:00
|
|
|
return refs_head_ref(get_main_ref_store(the_repository), fn, cb_data);
|
2016-04-07 19:02:48 +00:00
|
|
|
}
|
2016-04-07 19:02:49 +00:00
|
|
|
|
2017-03-20 16:33:08 +00:00
|
|
|
struct ref_iterator *refs_ref_iterator_begin(
|
|
|
|
struct ref_store *refs,
|
|
|
|
const char *prefix, int trim, int flags)
|
|
|
|
{
|
|
|
|
struct ref_iterator *iter;
|
|
|
|
|
2017-05-22 14:17:52 +00:00
|
|
|
if (ref_paranoia < 0)
|
|
|
|
ref_paranoia = git_env_bool("GIT_REF_PARANOIA", 0);
|
|
|
|
if (ref_paranoia)
|
|
|
|
flags |= DO_FOR_EACH_INCLUDE_BROKEN;
|
|
|
|
|
2017-03-20 16:33:08 +00:00
|
|
|
iter = refs->be->iterator_begin(refs, prefix, flags);
|
2017-05-22 14:17:36 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* `iterator_begin()` already takes care of prefix, but we
|
|
|
|
* might need to do some trimming:
|
|
|
|
*/
|
|
|
|
if (trim)
|
|
|
|
iter = prefix_ref_iterator_begin(iter, "", trim);
|
2017-03-20 16:33:08 +00:00
|
|
|
|
2017-09-13 17:15:55 +00:00
|
|
|
/* Sanity check for subclasses: */
|
|
|
|
if (!iter->ordered)
|
|
|
|
BUG("reference iterator is not ordered");
|
|
|
|
|
2017-03-20 16:33:08 +00:00
|
|
|
return iter;
|
|
|
|
}
|
|
|
|
|
do_for_each_ref(): reimplement using reference iteration
Use the reference iterator interface to implement do_for_each_ref().
Delete a bunch of code supporting the old for_each_ref() implementation.
And now that do_for_each_ref() is generic code (it is no longer tied to
the files backend), move it to refs.c.
The implementation is via a new function, do_for_each_ref_iterator(),
which takes a reference iterator as argument and calls a callback
function for each of the references in the iterator.
This change requires the current_ref performance hack for peel_ref() to
be implemented via ref_iterator_peel() rather than peel_entry() because
we don't have a ref_entry handy (it is hidden under three layers:
file_ref_iterator, merge_ref_iterator, and cache_ref_iterator). So:
* do_for_each_ref_iterator() records the active iterator in
current_ref_iter while it is running.
* peel_ref() checks whether current_ref_iter is pointing at the
requested reference. If so, it asks the iterator to peel the
reference (which it can do efficiently via its "peel" virtual
function). For extra safety, we do the optimization only if the
refname *addresses* are the same, not only if the refname *strings*
are the same, to forestall possible mixups between refnames that come
from different ref_iterators.
Please note that this optimization of peel_ref() is only available when
iterating via do_for_each_ref_iterator() (including all of the
for_each_ref() functions, which call it indirectly). It would be
complicated to implement a similar optimization when iterating directly
using a reference iterator, because multiple reference iterators can be
in use at the same time, with interleaved calls to
ref_iterator_advance(). (In fact we do exactly that in
merge_ref_iterator.)
But that is not necessary. peel_ref() is only called while iterating
over references. Callers who iterate using the for_each_ref() functions
benefit from the optimization described above. Callers who iterate using
reference iterators directly have access to the ref_iterator, so they
can call ref_iterator_peel() themselves to get an analogous optimization
in a more straightforward manner.
If we rewrite all callers to use the reference iteration API, then we
can remove the current_ref_iter hack permanently.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-06-18 04:15:16 +00:00
|
|
|
/*
|
|
|
|
* Call fn for each reference in the specified submodule for which the
|
|
|
|
* refname begins with prefix. If trim is non-zero, then trim that
|
|
|
|
* many characters off the beginning of each refname before passing
|
|
|
|
* the refname to fn. flags can be DO_FOR_EACH_INCLUDE_BROKEN to
|
|
|
|
* include broken references in the iteration. If fn ever returns a
|
|
|
|
* non-zero value, stop the iteration and return that value;
|
|
|
|
* otherwise, return 0.
|
|
|
|
*/
|
2018-08-20 18:24:16 +00:00
|
|
|
static int do_for_each_repo_ref(struct repository *r, const char *prefix,
|
|
|
|
each_repo_ref_fn fn, int trim, int flags,
|
|
|
|
void *cb_data)
|
|
|
|
{
|
|
|
|
struct ref_iterator *iter;
|
|
|
|
struct ref_store *refs = get_main_ref_store(r);
|
|
|
|
|
|
|
|
if (!refs)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
iter = refs_ref_iterator_begin(refs, prefix, trim, flags);
|
|
|
|
|
|
|
|
return do_for_each_repo_ref_iterator(r, iter, fn, cb_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct do_for_each_ref_help {
|
|
|
|
each_ref_fn *fn;
|
|
|
|
void *cb_data;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int do_for_each_ref_helper(struct repository *r,
|
|
|
|
const char *refname,
|
|
|
|
const struct object_id *oid,
|
|
|
|
int flags,
|
|
|
|
void *cb_data)
|
|
|
|
{
|
|
|
|
struct do_for_each_ref_help *hp = cb_data;
|
|
|
|
|
|
|
|
return hp->fn(refname, oid, flags, hp->cb_data);
|
|
|
|
}
|
|
|
|
|
2017-03-26 02:42:34 +00:00
|
|
|
static int do_for_each_ref(struct ref_store *refs, const char *prefix,
|
do_for_each_ref(): reimplement using reference iteration
Use the reference iterator interface to implement do_for_each_ref().
Delete a bunch of code supporting the old for_each_ref() implementation.
And now that do_for_each_ref() is generic code (it is no longer tied to
the files backend), move it to refs.c.
The implementation is via a new function, do_for_each_ref_iterator(),
which takes a reference iterator as argument and calls a callback
function for each of the references in the iterator.
This change requires the current_ref performance hack for peel_ref() to
be implemented via ref_iterator_peel() rather than peel_entry() because
we don't have a ref_entry handy (it is hidden under three layers:
file_ref_iterator, merge_ref_iterator, and cache_ref_iterator). So:
* do_for_each_ref_iterator() records the active iterator in
current_ref_iter while it is running.
* peel_ref() checks whether current_ref_iter is pointing at the
requested reference. If so, it asks the iterator to peel the
reference (which it can do efficiently via its "peel" virtual
function). For extra safety, we do the optimization only if the
refname *addresses* are the same, not only if the refname *strings*
are the same, to forestall possible mixups between refnames that come
from different ref_iterators.
Please note that this optimization of peel_ref() is only available when
iterating via do_for_each_ref_iterator() (including all of the
for_each_ref() functions, which call it indirectly). It would be
complicated to implement a similar optimization when iterating directly
using a reference iterator, because multiple reference iterators can be
in use at the same time, with interleaved calls to
ref_iterator_advance(). (In fact we do exactly that in
merge_ref_iterator.)
But that is not necessary. peel_ref() is only called while iterating
over references. Callers who iterate using the for_each_ref() functions
benefit from the optimization described above. Callers who iterate using
reference iterators directly have access to the ref_iterator, so they
can call ref_iterator_peel() themselves to get an analogous optimization
in a more straightforward manner.
If we rewrite all callers to use the reference iteration API, then we
can remove the current_ref_iter hack permanently.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-06-18 04:15:16 +00:00
|
|
|
each_ref_fn fn, int trim, int flags, void *cb_data)
|
|
|
|
{
|
|
|
|
struct ref_iterator *iter;
|
2018-08-20 18:24:16 +00:00
|
|
|
struct do_for_each_ref_help hp = { fn, cb_data };
|
do_for_each_ref(): reimplement using reference iteration
Use the reference iterator interface to implement do_for_each_ref().
Delete a bunch of code supporting the old for_each_ref() implementation.
And now that do_for_each_ref() is generic code (it is no longer tied to
the files backend), move it to refs.c.
The implementation is via a new function, do_for_each_ref_iterator(),
which takes a reference iterator as argument and calls a callback
function for each of the references in the iterator.
This change requires the current_ref performance hack for peel_ref() to
be implemented via ref_iterator_peel() rather than peel_entry() because
we don't have a ref_entry handy (it is hidden under three layers:
file_ref_iterator, merge_ref_iterator, and cache_ref_iterator). So:
* do_for_each_ref_iterator() records the active iterator in
current_ref_iter while it is running.
* peel_ref() checks whether current_ref_iter is pointing at the
requested reference. If so, it asks the iterator to peel the
reference (which it can do efficiently via its "peel" virtual
function). For extra safety, we do the optimization only if the
refname *addresses* are the same, not only if the refname *strings*
are the same, to forestall possible mixups between refnames that come
from different ref_iterators.
Please note that this optimization of peel_ref() is only available when
iterating via do_for_each_ref_iterator() (including all of the
for_each_ref() functions, which call it indirectly). It would be
complicated to implement a similar optimization when iterating directly
using a reference iterator, because multiple reference iterators can be
in use at the same time, with interleaved calls to
ref_iterator_advance(). (In fact we do exactly that in
merge_ref_iterator.)
But that is not necessary. peel_ref() is only called while iterating
over references. Callers who iterate using the for_each_ref() functions
benefit from the optimization described above. Callers who iterate using
reference iterators directly have access to the ref_iterator, so they
can call ref_iterator_peel() themselves to get an analogous optimization
in a more straightforward manner.
If we rewrite all callers to use the reference iteration API, then we
can remove the current_ref_iter hack permanently.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-06-18 04:15:16 +00:00
|
|
|
|
2016-09-04 16:08:11 +00:00
|
|
|
if (!refs)
|
|
|
|
return 0;
|
|
|
|
|
2017-03-20 16:33:08 +00:00
|
|
|
iter = refs_ref_iterator_begin(refs, prefix, trim, flags);
|
do_for_each_ref(): reimplement using reference iteration
Use the reference iterator interface to implement do_for_each_ref().
Delete a bunch of code supporting the old for_each_ref() implementation.
And now that do_for_each_ref() is generic code (it is no longer tied to
the files backend), move it to refs.c.
The implementation is via a new function, do_for_each_ref_iterator(),
which takes a reference iterator as argument and calls a callback
function for each of the references in the iterator.
This change requires the current_ref performance hack for peel_ref() to
be implemented via ref_iterator_peel() rather than peel_entry() because
we don't have a ref_entry handy (it is hidden under three layers:
file_ref_iterator, merge_ref_iterator, and cache_ref_iterator). So:
* do_for_each_ref_iterator() records the active iterator in
current_ref_iter while it is running.
* peel_ref() checks whether current_ref_iter is pointing at the
requested reference. If so, it asks the iterator to peel the
reference (which it can do efficiently via its "peel" virtual
function). For extra safety, we do the optimization only if the
refname *addresses* are the same, not only if the refname *strings*
are the same, to forestall possible mixups between refnames that come
from different ref_iterators.
Please note that this optimization of peel_ref() is only available when
iterating via do_for_each_ref_iterator() (including all of the
for_each_ref() functions, which call it indirectly). It would be
complicated to implement a similar optimization when iterating directly
using a reference iterator, because multiple reference iterators can be
in use at the same time, with interleaved calls to
ref_iterator_advance(). (In fact we do exactly that in
merge_ref_iterator.)
But that is not necessary. peel_ref() is only called while iterating
over references. Callers who iterate using the for_each_ref() functions
benefit from the optimization described above. Callers who iterate using
reference iterators directly have access to the ref_iterator, so they
can call ref_iterator_peel() themselves to get an analogous optimization
in a more straightforward manner.
If we rewrite all callers to use the reference iteration API, then we
can remove the current_ref_iter hack permanently.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-06-18 04:15:16 +00:00
|
|
|
|
2018-08-20 18:24:16 +00:00
|
|
|
return do_for_each_repo_ref_iterator(the_repository, iter,
|
|
|
|
do_for_each_ref_helper, &hp);
|
do_for_each_ref(): reimplement using reference iteration
Use the reference iterator interface to implement do_for_each_ref().
Delete a bunch of code supporting the old for_each_ref() implementation.
And now that do_for_each_ref() is generic code (it is no longer tied to
the files backend), move it to refs.c.
The implementation is via a new function, do_for_each_ref_iterator(),
which takes a reference iterator as argument and calls a callback
function for each of the references in the iterator.
This change requires the current_ref performance hack for peel_ref() to
be implemented via ref_iterator_peel() rather than peel_entry() because
we don't have a ref_entry handy (it is hidden under three layers:
file_ref_iterator, merge_ref_iterator, and cache_ref_iterator). So:
* do_for_each_ref_iterator() records the active iterator in
current_ref_iter while it is running.
* peel_ref() checks whether current_ref_iter is pointing at the
requested reference. If so, it asks the iterator to peel the
reference (which it can do efficiently via its "peel" virtual
function). For extra safety, we do the optimization only if the
refname *addresses* are the same, not only if the refname *strings*
are the same, to forestall possible mixups between refnames that come
from different ref_iterators.
Please note that this optimization of peel_ref() is only available when
iterating via do_for_each_ref_iterator() (including all of the
for_each_ref() functions, which call it indirectly). It would be
complicated to implement a similar optimization when iterating directly
using a reference iterator, because multiple reference iterators can be
in use at the same time, with interleaved calls to
ref_iterator_advance(). (In fact we do exactly that in
merge_ref_iterator.)
But that is not necessary. peel_ref() is only called while iterating
over references. Callers who iterate using the for_each_ref() functions
benefit from the optimization described above. Callers who iterate using
reference iterators directly have access to the ref_iterator, so they
can call ref_iterator_peel() themselves to get an analogous optimization
in a more straightforward manner.
If we rewrite all callers to use the reference iteration API, then we
can remove the current_ref_iter hack permanently.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-06-18 04:15:16 +00:00
|
|
|
}
|
|
|
|
|
2017-03-26 02:42:34 +00:00
|
|
|
int refs_for_each_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
|
|
|
|
{
|
|
|
|
return do_for_each_ref(refs, "", fn, 0, 0, cb_data);
|
|
|
|
}
|
|
|
|
|
2016-04-07 19:02:49 +00:00
|
|
|
int for_each_ref(each_ref_fn fn, void *cb_data)
|
|
|
|
{
|
2018-04-12 00:21:09 +00:00
|
|
|
return refs_for_each_ref(get_main_ref_store(the_repository), fn, cb_data);
|
2016-04-07 19:02:49 +00:00
|
|
|
}
|
|
|
|
|
2017-03-26 02:42:34 +00:00
|
|
|
int refs_for_each_ref_in(struct ref_store *refs, const char *prefix,
|
|
|
|
each_ref_fn fn, void *cb_data)
|
|
|
|
{
|
|
|
|
return do_for_each_ref(refs, prefix, fn, strlen(prefix), 0, cb_data);
|
2016-04-07 19:02:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
|
|
|
|
{
|
2018-04-12 00:21:09 +00:00
|
|
|
return refs_for_each_ref_in(get_main_ref_store(the_repository), prefix, fn, cb_data);
|
2016-04-07 19:02:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, unsigned int broken)
|
|
|
|
{
|
|
|
|
unsigned int flag = 0;
|
|
|
|
|
|
|
|
if (broken)
|
|
|
|
flag = DO_FOR_EACH_INCLUDE_BROKEN;
|
2018-04-12 00:21:09 +00:00
|
|
|
return do_for_each_ref(get_main_ref_store(the_repository),
|
2017-03-26 02:42:34 +00:00
|
|
|
prefix, fn, 0, flag, cb_data);
|
2016-04-07 19:02:49 +00:00
|
|
|
}
|
|
|
|
|
2017-08-23 12:36:56 +00:00
|
|
|
int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,
|
|
|
|
each_ref_fn fn, void *cb_data,
|
|
|
|
unsigned int broken)
|
2017-06-18 13:39:41 +00:00
|
|
|
{
|
|
|
|
unsigned int flag = 0;
|
|
|
|
|
|
|
|
if (broken)
|
|
|
|
flag = DO_FOR_EACH_INCLUDE_BROKEN;
|
2017-08-23 12:36:56 +00:00
|
|
|
return do_for_each_ref(refs, prefix, fn, 0, flag, cb_data);
|
2017-06-18 13:39:41 +00:00
|
|
|
}
|
|
|
|
|
2018-08-20 18:24:19 +00:00
|
|
|
int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_data)
|
2016-04-07 19:02:49 +00:00
|
|
|
{
|
2018-08-20 18:24:19 +00:00
|
|
|
return do_for_each_repo_ref(r, git_replace_ref_base, fn,
|
|
|
|
strlen(git_replace_ref_base),
|
|
|
|
DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
|
2016-04-07 19:02:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)
|
|
|
|
{
|
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
int ret;
|
|
|
|
strbuf_addf(&buf, "%srefs/", get_git_namespace());
|
2018-04-12 00:21:09 +00:00
|
|
|
ret = do_for_each_ref(get_main_ref_store(the_repository),
|
2017-03-26 02:42:34 +00:00
|
|
|
buf.buf, fn, 0, 0, cb_data);
|
2016-04-07 19:02:49 +00:00
|
|
|
strbuf_release(&buf);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-03-26 02:42:34 +00:00
|
|
|
int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
|
2016-04-07 19:02:49 +00:00
|
|
|
{
|
2017-03-26 02:42:34 +00:00
|
|
|
return do_for_each_ref(refs, "", fn, 0,
|
2016-04-07 19:02:49 +00:00
|
|
|
DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
|
|
|
|
}
|
2016-04-07 19:03:10 +00:00
|
|
|
|
2017-03-26 02:42:34 +00:00
|
|
|
int for_each_rawref(each_ref_fn fn, void *cb_data)
|
|
|
|
{
|
2018-04-12 00:21:09 +00:00
|
|
|
return refs_for_each_rawref(get_main_ref_store(the_repository), fn, cb_data);
|
2017-03-26 02:42:34 +00:00
|
|
|
}
|
|
|
|
|
2017-03-20 16:33:07 +00:00
|
|
|
int refs_read_raw_ref(struct ref_store *ref_store,
|
2017-10-15 22:07:11 +00:00
|
|
|
const char *refname, struct object_id *oid,
|
2017-03-20 16:33:07 +00:00
|
|
|
struct strbuf *referent, unsigned int *type)
|
|
|
|
{
|
2017-10-15 22:07:11 +00:00
|
|
|
return ref_store->be->read_raw_ref(ref_store, refname, oid, referent, type);
|
2017-03-20 16:33:07 +00:00
|
|
|
}
|
|
|
|
|
2016-04-07 19:03:10 +00:00
|
|
|
/* This function needs to return a meaningful errno on failure */
|
2017-03-26 02:42:34 +00:00
|
|
|
const char *refs_resolve_ref_unsafe(struct ref_store *refs,
|
2017-02-09 20:53:52 +00:00
|
|
|
const char *refname,
|
|
|
|
int resolve_flags,
|
refs: convert resolve_ref_unsafe to struct object_id
Convert resolve_ref_unsafe to take a pointer to struct object_id by
converting one remaining caller to use struct object_id, removing the
temporary NULL pointer check in expand_ref, converting the declaration
and definition, and applying the following semantic patch:
@@
expression E1, E2, E3, E4;
@@
- resolve_ref_unsafe(E1, E2, E3.hash, E4)
+ resolve_ref_unsafe(E1, E2, &E3, E4)
@@
expression E1, E2, E3, E4;
@@
- resolve_ref_unsafe(E1, E2, E3->hash, E4)
+ resolve_ref_unsafe(E1, E2, E3, E4)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-10-15 22:07:09 +00:00
|
|
|
struct object_id *oid, int *flags)
|
2016-04-07 19:03:10 +00:00
|
|
|
{
|
|
|
|
static struct strbuf sb_refname = STRBUF_INIT;
|
2017-09-23 09:41:45 +00:00
|
|
|
struct object_id unused_oid;
|
2016-04-07 19:03:10 +00:00
|
|
|
int unused_flags;
|
|
|
|
int symref_count;
|
|
|
|
|
refs: convert resolve_ref_unsafe to struct object_id
Convert resolve_ref_unsafe to take a pointer to struct object_id by
converting one remaining caller to use struct object_id, removing the
temporary NULL pointer check in expand_ref, converting the declaration
and definition, and applying the following semantic patch:
@@
expression E1, E2, E3, E4;
@@
- resolve_ref_unsafe(E1, E2, E3.hash, E4)
+ resolve_ref_unsafe(E1, E2, &E3, E4)
@@
expression E1, E2, E3, E4;
@@
- resolve_ref_unsafe(E1, E2, E3->hash, E4)
+ resolve_ref_unsafe(E1, E2, E3, E4)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-10-15 22:07:09 +00:00
|
|
|
if (!oid)
|
|
|
|
oid = &unused_oid;
|
2016-04-07 19:03:10 +00:00
|
|
|
if (!flags)
|
|
|
|
flags = &unused_flags;
|
|
|
|
|
|
|
|
*flags = 0;
|
|
|
|
|
|
|
|
if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
|
|
|
|
if (!(resolve_flags & RESOLVE_REF_ALLOW_BAD_NAME) ||
|
|
|
|
!refname_is_safe(refname)) {
|
|
|
|
errno = EINVAL;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* dwim_ref() uses REF_ISBROKEN to distinguish between
|
|
|
|
* missing refs and refs that were present but invalid,
|
|
|
|
* to complain about the latter to stderr.
|
|
|
|
*
|
|
|
|
* We don't know whether the ref exists, so don't set
|
|
|
|
* REF_ISBROKEN yet.
|
|
|
|
*/
|
|
|
|
*flags |= REF_BAD_NAME;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (symref_count = 0; symref_count < SYMREF_MAXDEPTH; symref_count++) {
|
|
|
|
unsigned int read_flags = 0;
|
|
|
|
|
2017-03-20 16:33:07 +00:00
|
|
|
if (refs_read_raw_ref(refs, refname,
|
2017-10-15 22:07:11 +00:00
|
|
|
oid, &sb_refname, &read_flags)) {
|
2016-04-07 19:03:10 +00:00
|
|
|
*flags |= read_flags;
|
refs_resolve_ref_unsafe: handle d/f conflicts for writes
If our call to refs_read_raw_ref() fails, we check errno to
see if the ref is simply missing, or if we encountered a
more serious error. If it's just missing, then in "write"
mode (i.e., when RESOLVE_REFS_READING is not set), this is
perfectly fine.
However, checking for ENOENT isn't sufficient to catch all
missing-ref cases. In the filesystem backend, we may also
see EISDIR when we try to resolve "a" and "a/b" exists.
Likewise, we may see ENOTDIR if we try to resolve "a/b" and
"a" exists. In both of those cases, we know that our
resolved ref doesn't exist, but we return an error (rather
than reporting the refname and returning a null sha1).
This has been broken for a long time, but nobody really
noticed because the next step after resolving without the
READING flag is usually to lock the ref and write it. But in
both of those cases, the write will fail with the same
errno due to the directory/file conflict.
There are two cases where we can notice this, though:
1. If we try to write "a" and there's a leftover directory
already at "a", even though there is no ref "a/b". The
actual write is smart enough to move the empty "a" out
of the way.
This is reasonably rare, if only because the writing
code has to do an independent resolution before trying
its write (because the actual update_ref() code handles
this case fine). The notes-merge code does this, and
before the fix in the prior commit t3308 erroneously
expected this case to fail.
2. When resolving symbolic refs, we typically do not use
the READING flag because we want to resolve even
symrefs that point to unborn refs. Even if those unborn
refs could not actually be written because of d/f
conflicts with existing refs.
You can see this by asking "git symbolic-ref" to report
the target of a symref pointing past a d/f conflict.
We can fix the problem by recognizing the other "missing"
errnos and treating them like ENOENT. This should be safe to
do even for callers who are then going to actually write the
ref, because the actual writing process will fail if the d/f
conflict is a real one (and t1404 checks these cases).
Arguably this should be the responsibility of the
files-backend to normalize all "missing ref" errors into
ENOENT (since something like EISDIR may not be meaningful at
all to a database backend). However other callers of
refs_read_raw_ref() may actually care about the distinction;
putting this into resolve_ref() is the minimal fix for now.
The new tests in t1401 use git-symbolic-ref, which is the
most direct way to check the resolution by itself.
Interestingly we actually had a test that setup this case
already, but we only used it to verify that the funny state
could be overwritten, not that it could be resolved.
We also add a new test in t3200, as "branch -m" was the
original motivation for looking into this. What happens is
this:
0. HEAD is pointing to branch "a"
1. The user asks to rename "a" to "a/b".
2. We create "a/b" and delete "a".
3. We then try to update any worktree HEADs that point to
the renamed ref (including the main repo HEAD). To do
that, we have to resolve each HEAD. But now our HEAD is
pointing at "a", and we get EISDIR due to the loose
"a/b". As a result, we think there is no HEAD, and we
do not update it. It now points to the bogus "a".
Interestingly this case used to work, but only accidentally.
Before 31824d180d (branch: fix branch renaming not updating
HEADs correctly, 2017-08-24), we'd update any HEAD which we
couldn't resolve. That was wrong, but it papered over the
fact that we were incorrectly failing to resolve HEAD.
So while the bug demonstrated by the git-symbolic-ref is
quite old, the regression to "branch -m" is recent.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-10-06 14:42:17 +00:00
|
|
|
|
|
|
|
/* In reading mode, refs must eventually resolve */
|
|
|
|
if (resolve_flags & RESOLVE_REF_READING)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Otherwise a missing ref is OK. But the files backend
|
|
|
|
* may show errors besides ENOENT if there are
|
|
|
|
* similarly-named refs.
|
|
|
|
*/
|
|
|
|
if (errno != ENOENT &&
|
|
|
|
errno != EISDIR &&
|
|
|
|
errno != ENOTDIR)
|
2016-04-07 19:03:10 +00:00
|
|
|
return NULL;
|
refs_resolve_ref_unsafe: handle d/f conflicts for writes
If our call to refs_read_raw_ref() fails, we check errno to
see if the ref is simply missing, or if we encountered a
more serious error. If it's just missing, then in "write"
mode (i.e., when RESOLVE_REFS_READING is not set), this is
perfectly fine.
However, checking for ENOENT isn't sufficient to catch all
missing-ref cases. In the filesystem backend, we may also
see EISDIR when we try to resolve "a" and "a/b" exists.
Likewise, we may see ENOTDIR if we try to resolve "a/b" and
"a" exists. In both of those cases, we know that our
resolved ref doesn't exist, but we return an error (rather
than reporting the refname and returning a null sha1).
This has been broken for a long time, but nobody really
noticed because the next step after resolving without the
READING flag is usually to lock the ref and write it. But in
both of those cases, the write will fail with the same
errno due to the directory/file conflict.
There are two cases where we can notice this, though:
1. If we try to write "a" and there's a leftover directory
already at "a", even though there is no ref "a/b". The
actual write is smart enough to move the empty "a" out
of the way.
This is reasonably rare, if only because the writing
code has to do an independent resolution before trying
its write (because the actual update_ref() code handles
this case fine). The notes-merge code does this, and
before the fix in the prior commit t3308 erroneously
expected this case to fail.
2. When resolving symbolic refs, we typically do not use
the READING flag because we want to resolve even
symrefs that point to unborn refs. Even if those unborn
refs could not actually be written because of d/f
conflicts with existing refs.
You can see this by asking "git symbolic-ref" to report
the target of a symref pointing past a d/f conflict.
We can fix the problem by recognizing the other "missing"
errnos and treating them like ENOENT. This should be safe to
do even for callers who are then going to actually write the
ref, because the actual writing process will fail if the d/f
conflict is a real one (and t1404 checks these cases).
Arguably this should be the responsibility of the
files-backend to normalize all "missing ref" errors into
ENOENT (since something like EISDIR may not be meaningful at
all to a database backend). However other callers of
refs_read_raw_ref() may actually care about the distinction;
putting this into resolve_ref() is the minimal fix for now.
The new tests in t1401 use git-symbolic-ref, which is the
most direct way to check the resolution by itself.
Interestingly we actually had a test that setup this case
already, but we only used it to verify that the funny state
could be overwritten, not that it could be resolved.
We also add a new test in t3200, as "branch -m" was the
original motivation for looking into this. What happens is
this:
0. HEAD is pointing to branch "a"
1. The user asks to rename "a" to "a/b".
2. We create "a/b" and delete "a".
3. We then try to update any worktree HEADs that point to
the renamed ref (including the main repo HEAD). To do
that, we have to resolve each HEAD. But now our HEAD is
pointing at "a", and we get EISDIR due to the loose
"a/b". As a result, we think there is no HEAD, and we
do not update it. It now points to the bogus "a".
Interestingly this case used to work, but only accidentally.
Before 31824d180d (branch: fix branch renaming not updating
HEADs correctly, 2017-08-24), we'd update any HEAD which we
couldn't resolve. That was wrong, but it papered over the
fact that we were incorrectly failing to resolve HEAD.
So while the bug demonstrated by the git-symbolic-ref is
quite old, the regression to "branch -m" is recent.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-10-06 14:42:17 +00:00
|
|
|
|
refs: convert resolve_ref_unsafe to struct object_id
Convert resolve_ref_unsafe to take a pointer to struct object_id by
converting one remaining caller to use struct object_id, removing the
temporary NULL pointer check in expand_ref, converting the declaration
and definition, and applying the following semantic patch:
@@
expression E1, E2, E3, E4;
@@
- resolve_ref_unsafe(E1, E2, E3.hash, E4)
+ resolve_ref_unsafe(E1, E2, &E3, E4)
@@
expression E1, E2, E3, E4;
@@
- resolve_ref_unsafe(E1, E2, E3->hash, E4)
+ resolve_ref_unsafe(E1, E2, E3, E4)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-10-15 22:07:09 +00:00
|
|
|
oidclr(oid);
|
2016-04-07 19:03:10 +00:00
|
|
|
if (*flags & REF_BAD_NAME)
|
|
|
|
*flags |= REF_ISBROKEN;
|
|
|
|
return refname;
|
|
|
|
}
|
|
|
|
|
|
|
|
*flags |= read_flags;
|
|
|
|
|
|
|
|
if (!(read_flags & REF_ISSYMREF)) {
|
|
|
|
if (*flags & REF_BAD_NAME) {
|
refs: convert resolve_ref_unsafe to struct object_id
Convert resolve_ref_unsafe to take a pointer to struct object_id by
converting one remaining caller to use struct object_id, removing the
temporary NULL pointer check in expand_ref, converting the declaration
and definition, and applying the following semantic patch:
@@
expression E1, E2, E3, E4;
@@
- resolve_ref_unsafe(E1, E2, E3.hash, E4)
+ resolve_ref_unsafe(E1, E2, &E3, E4)
@@
expression E1, E2, E3, E4;
@@
- resolve_ref_unsafe(E1, E2, E3->hash, E4)
+ resolve_ref_unsafe(E1, E2, E3, E4)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-10-15 22:07:09 +00:00
|
|
|
oidclr(oid);
|
2016-04-07 19:03:10 +00:00
|
|
|
*flags |= REF_ISBROKEN;
|
|
|
|
}
|
|
|
|
return refname;
|
|
|
|
}
|
|
|
|
|
|
|
|
refname = sb_refname.buf;
|
|
|
|
if (resolve_flags & RESOLVE_REF_NO_RECURSE) {
|
refs: convert resolve_ref_unsafe to struct object_id
Convert resolve_ref_unsafe to take a pointer to struct object_id by
converting one remaining caller to use struct object_id, removing the
temporary NULL pointer check in expand_ref, converting the declaration
and definition, and applying the following semantic patch:
@@
expression E1, E2, E3, E4;
@@
- resolve_ref_unsafe(E1, E2, E3.hash, E4)
+ resolve_ref_unsafe(E1, E2, &E3, E4)
@@
expression E1, E2, E3, E4;
@@
- resolve_ref_unsafe(E1, E2, E3->hash, E4)
+ resolve_ref_unsafe(E1, E2, E3, E4)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-10-15 22:07:09 +00:00
|
|
|
oidclr(oid);
|
2016-04-07 19:03:10 +00:00
|
|
|
return refname;
|
|
|
|
}
|
|
|
|
if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
|
|
|
|
if (!(resolve_flags & RESOLVE_REF_ALLOW_BAD_NAME) ||
|
|
|
|
!refname_is_safe(refname)) {
|
|
|
|
errno = EINVAL;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
*flags |= REF_ISBROKEN | REF_BAD_NAME;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
errno = ELOOP;
|
|
|
|
return NULL;
|
|
|
|
}
|
2016-09-04 16:08:11 +00:00
|
|
|
|
2016-09-04 16:08:41 +00:00
|
|
|
/* backend functions */
|
|
|
|
int refs_init_db(struct strbuf *err)
|
|
|
|
{
|
2018-04-12 00:21:09 +00:00
|
|
|
struct ref_store *refs = get_main_ref_store(the_repository);
|
2016-09-04 16:08:41 +00:00
|
|
|
|
|
|
|
return refs->be->init_db(refs, err);
|
|
|
|
}
|
|
|
|
|
2016-09-04 16:08:21 +00:00
|
|
|
const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
|
refs: convert resolve_ref_unsafe to struct object_id
Convert resolve_ref_unsafe to take a pointer to struct object_id by
converting one remaining caller to use struct object_id, removing the
temporary NULL pointer check in expand_ref, converting the declaration
and definition, and applying the following semantic patch:
@@
expression E1, E2, E3, E4;
@@
- resolve_ref_unsafe(E1, E2, E3.hash, E4)
+ resolve_ref_unsafe(E1, E2, &E3, E4)
@@
expression E1, E2, E3, E4;
@@
- resolve_ref_unsafe(E1, E2, E3->hash, E4)
+ resolve_ref_unsafe(E1, E2, E3, E4)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-10-15 22:07:09 +00:00
|
|
|
struct object_id *oid, int *flags)
|
2016-09-04 16:08:21 +00:00
|
|
|
{
|
2018-04-12 00:21:09 +00:00
|
|
|
return refs_resolve_ref_unsafe(get_main_ref_store(the_repository), refname,
|
refs: convert resolve_ref_unsafe to struct object_id
Convert resolve_ref_unsafe to take a pointer to struct object_id by
converting one remaining caller to use struct object_id, removing the
temporary NULL pointer check in expand_ref, converting the declaration
and definition, and applying the following semantic patch:
@@
expression E1, E2, E3, E4;
@@
- resolve_ref_unsafe(E1, E2, E3.hash, E4)
+ resolve_ref_unsafe(E1, E2, &E3, E4)
@@
expression E1, E2, E3, E4;
@@
- resolve_ref_unsafe(E1, E2, E3->hash, E4)
+ resolve_ref_unsafe(E1, E2, E3, E4)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-10-15 22:07:09 +00:00
|
|
|
resolve_flags, oid, flags);
|
2016-09-04 16:08:21 +00:00
|
|
|
}
|
|
|
|
|
2016-09-04 16:08:24 +00:00
|
|
|
int resolve_gitlink_ref(const char *submodule, const char *refname,
|
refs: convert resolve_gitlink_ref to struct object_id
Convert the declaration and definition of resolve_gitlink_ref to use
struct object_id and apply the following semantic patch:
@@
expression E1, E2, E3;
@@
- resolve_gitlink_ref(E1, E2, E3.hash)
+ resolve_gitlink_ref(E1, E2, &E3)
@@
expression E1, E2, E3;
@@
- resolve_gitlink_ref(E1, E2, E3->hash)
+ resolve_gitlink_ref(E1, E2, E3)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-10-15 22:07:07 +00:00
|
|
|
struct object_id *oid)
|
2016-09-04 16:08:22 +00:00
|
|
|
{
|
|
|
|
struct ref_store *refs;
|
|
|
|
int flags;
|
|
|
|
|
2017-08-23 12:36:54 +00:00
|
|
|
refs = get_submodule_ref_store(submodule);
|
2016-09-04 16:08:23 +00:00
|
|
|
|
2016-09-04 16:08:22 +00:00
|
|
|
if (!refs)
|
|
|
|
return -1;
|
|
|
|
|
refs: convert resolve_ref_unsafe to struct object_id
Convert resolve_ref_unsafe to take a pointer to struct object_id by
converting one remaining caller to use struct object_id, removing the
temporary NULL pointer check in expand_ref, converting the declaration
and definition, and applying the following semantic patch:
@@
expression E1, E2, E3, E4;
@@
- resolve_ref_unsafe(E1, E2, E3.hash, E4)
+ resolve_ref_unsafe(E1, E2, &E3, E4)
@@
expression E1, E2, E3, E4;
@@
- resolve_ref_unsafe(E1, E2, E3->hash, E4)
+ resolve_ref_unsafe(E1, E2, E3, E4)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-10-15 22:07:09 +00:00
|
|
|
if (!refs_resolve_ref_unsafe(refs, refname, 0, oid, &flags) ||
|
refs: convert resolve_gitlink_ref to struct object_id
Convert the declaration and definition of resolve_gitlink_ref to use
struct object_id and apply the following semantic patch:
@@
expression E1, E2, E3;
@@
- resolve_gitlink_ref(E1, E2, E3.hash)
+ resolve_gitlink_ref(E1, E2, &E3)
@@
expression E1, E2, E3;
@@
- resolve_gitlink_ref(E1, E2, E3->hash)
+ resolve_gitlink_ref(E1, E2, E3)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-10-15 22:07:07 +00:00
|
|
|
is_null_oid(oid))
|
2016-09-04 16:08:22 +00:00
|
|
|
return -1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-04-04 10:21:20 +00:00
|
|
|
struct ref_store_hash_entry
|
2017-02-10 11:16:15 +00:00
|
|
|
{
|
|
|
|
struct hashmap_entry ent; /* must be the first member! */
|
|
|
|
|
|
|
|
struct ref_store *refs;
|
|
|
|
|
2017-04-04 10:21:20 +00:00
|
|
|
/* NUL-terminated identifier of the ref store: */
|
|
|
|
char name[FLEX_ARRAY];
|
2017-02-10 11:16:15 +00:00
|
|
|
};
|
|
|
|
|
2017-06-30 19:14:05 +00:00
|
|
|
static int ref_store_hash_cmp(const void *unused_cmp_data,
|
|
|
|
const void *entry, const void *entry_or_key,
|
2017-02-10 11:16:15 +00:00
|
|
|
const void *keydata)
|
|
|
|
{
|
2017-04-04 10:21:20 +00:00
|
|
|
const struct ref_store_hash_entry *e1 = entry, *e2 = entry_or_key;
|
|
|
|
const char *name = keydata ? keydata : e2->name;
|
2017-02-10 11:16:15 +00:00
|
|
|
|
2017-04-04 10:21:20 +00:00
|
|
|
return strcmp(e1->name, name);
|
2017-02-10 11:16:15 +00:00
|
|
|
}
|
|
|
|
|
2017-04-04 10:21:20 +00:00
|
|
|
static struct ref_store_hash_entry *alloc_ref_store_hash_entry(
|
|
|
|
const char *name, struct ref_store *refs)
|
2017-02-10 11:16:15 +00:00
|
|
|
{
|
2017-04-04 10:21:20 +00:00
|
|
|
struct ref_store_hash_entry *entry;
|
2017-02-10 11:16:15 +00:00
|
|
|
|
2017-04-04 10:21:20 +00:00
|
|
|
FLEX_ALLOC_STR(entry, name, name);
|
|
|
|
hashmap_entry_init(entry, strhash(name));
|
2017-02-10 11:16:15 +00:00
|
|
|
entry->refs = refs;
|
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* A hashmap of ref_stores, stored by submodule name: */
|
|
|
|
static struct hashmap submodule_ref_stores;
|
2016-09-04 16:08:11 +00:00
|
|
|
|
2017-04-24 10:01:22 +00:00
|
|
|
/* A hashmap of ref_stores, stored by worktree id: */
|
|
|
|
static struct hashmap worktree_ref_stores;
|
|
|
|
|
2017-02-10 11:16:12 +00:00
|
|
|
/*
|
2017-04-04 10:21:20 +00:00
|
|
|
* Look up a ref store by name. If that ref_store hasn't been
|
|
|
|
* registered yet, return NULL.
|
2017-02-10 11:16:12 +00:00
|
|
|
*/
|
2017-04-04 10:21:20 +00:00
|
|
|
static struct ref_store *lookup_ref_store_map(struct hashmap *map,
|
|
|
|
const char *name)
|
2016-09-04 16:08:11 +00:00
|
|
|
{
|
2017-04-04 10:21:20 +00:00
|
|
|
struct ref_store_hash_entry *entry;
|
2016-09-04 16:08:11 +00:00
|
|
|
|
2017-04-04 10:21:20 +00:00
|
|
|
if (!map->tablesize)
|
2017-02-10 11:16:15 +00:00
|
|
|
/* It's initialized on demand in register_ref_store(). */
|
|
|
|
return NULL;
|
2017-02-10 11:16:11 +00:00
|
|
|
|
2017-04-04 10:21:20 +00:00
|
|
|
entry = hashmap_get_from_hash(map, strhash(name), name);
|
2017-02-10 11:16:15 +00:00
|
|
|
return entry ? entry->refs : NULL;
|
2016-09-04 16:08:11 +00:00
|
|
|
}
|
|
|
|
|
2017-02-10 11:16:12 +00:00
|
|
|
/*
|
|
|
|
* Create, record, and return a ref_store instance for the specified
|
2017-03-26 02:42:31 +00:00
|
|
|
* gitdir.
|
2017-02-10 11:16:12 +00:00
|
|
|
*/
|
2017-03-26 02:42:32 +00:00
|
|
|
static struct ref_store *ref_store_init(const char *gitdir,
|
|
|
|
unsigned int flags)
|
2016-09-04 16:08:11 +00:00
|
|
|
{
|
|
|
|
const char *be_name = "files";
|
|
|
|
struct ref_storage_be *be = find_ref_storage_backend(be_name);
|
2017-02-10 11:16:14 +00:00
|
|
|
struct ref_store *refs;
|
2016-09-04 16:08:11 +00:00
|
|
|
|
|
|
|
if (!be)
|
2018-05-02 09:38:39 +00:00
|
|
|
BUG("reference backend %s is unknown", be_name);
|
2016-09-04 16:08:11 +00:00
|
|
|
|
2017-03-26 02:42:32 +00:00
|
|
|
refs = be->init(gitdir, flags);
|
2017-02-10 11:16:14 +00:00
|
|
|
return refs;
|
2016-09-04 16:08:11 +00:00
|
|
|
}
|
|
|
|
|
2018-04-12 00:21:14 +00:00
|
|
|
struct ref_store *get_main_ref_store(struct repository *r)
|
2017-03-26 02:42:25 +00:00
|
|
|
{
|
2018-04-12 00:21:14 +00:00
|
|
|
if (r->refs)
|
|
|
|
return r->refs;
|
2017-03-26 02:42:25 +00:00
|
|
|
|
2018-05-18 22:25:53 +00:00
|
|
|
if (!r->gitdir)
|
|
|
|
BUG("attempting to get main_ref_store outside of repository");
|
|
|
|
|
2018-04-12 00:21:14 +00:00
|
|
|
r->refs = ref_store_init(r->gitdir, REF_STORE_ALL_CAPS);
|
|
|
|
return r->refs;
|
2017-03-26 02:42:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2017-04-04 10:21:20 +00:00
|
|
|
* Associate a ref store with a name. It is a fatal error to call this
|
|
|
|
* function twice for the same name.
|
2017-03-26 02:42:28 +00:00
|
|
|
*/
|
2017-04-04 10:21:20 +00:00
|
|
|
static void register_ref_store_map(struct hashmap *map,
|
|
|
|
const char *type,
|
|
|
|
struct ref_store *refs,
|
|
|
|
const char *name)
|
2017-03-26 02:42:28 +00:00
|
|
|
{
|
2017-04-04 10:21:20 +00:00
|
|
|
if (!map->tablesize)
|
2017-06-30 19:14:05 +00:00
|
|
|
hashmap_init(map, ref_store_hash_cmp, NULL, 0);
|
2017-03-26 02:42:28 +00:00
|
|
|
|
2017-04-04 10:21:20 +00:00
|
|
|
if (hashmap_put(map, alloc_ref_store_hash_entry(name, refs)))
|
2018-05-02 09:38:39 +00:00
|
|
|
BUG("%s ref_store '%s' initialized twice", type, name);
|
2017-03-26 02:42:25 +00:00
|
|
|
}
|
|
|
|
|
2017-03-26 02:42:33 +00:00
|
|
|
struct ref_store *get_submodule_ref_store(const char *submodule)
|
2016-09-04 16:08:11 +00:00
|
|
|
{
|
2017-03-26 02:42:27 +00:00
|
|
|
struct strbuf submodule_sb = STRBUF_INIT;
|
2016-09-04 16:08:11 +00:00
|
|
|
struct ref_store *refs;
|
2017-08-23 12:36:54 +00:00
|
|
|
char *to_free = NULL;
|
|
|
|
size_t len;
|
2016-09-04 16:08:11 +00:00
|
|
|
|
2017-08-23 12:37:03 +00:00
|
|
|
if (!submodule)
|
|
|
|
return NULL;
|
|
|
|
|
2017-08-23 12:37:04 +00:00
|
|
|
len = strlen(submodule);
|
|
|
|
while (len && is_dir_sep(submodule[len - 1]))
|
|
|
|
len--;
|
|
|
|
if (!len)
|
|
|
|
return NULL;
|
2016-09-04 16:08:11 +00:00
|
|
|
|
2017-08-23 12:36:54 +00:00
|
|
|
if (submodule[len])
|
|
|
|
/* We need to strip off one or more trailing slashes */
|
|
|
|
submodule = to_free = xmemdupz(submodule, len);
|
2016-09-04 16:08:11 +00:00
|
|
|
|
2017-04-04 10:21:20 +00:00
|
|
|
refs = lookup_ref_store_map(&submodule_ref_stores, submodule);
|
2017-03-26 02:42:27 +00:00
|
|
|
if (refs)
|
2017-08-23 12:36:53 +00:00
|
|
|
goto done;
|
2016-09-04 16:08:11 +00:00
|
|
|
|
2017-03-26 02:42:27 +00:00
|
|
|
strbuf_addstr(&submodule_sb, submodule);
|
2017-08-23 12:36:53 +00:00
|
|
|
if (!is_nonbare_repository_dir(&submodule_sb))
|
|
|
|
goto done;
|
2016-09-04 16:08:11 +00:00
|
|
|
|
2017-08-23 12:36:53 +00:00
|
|
|
if (submodule_to_gitdir(&submodule_sb, submodule))
|
|
|
|
goto done;
|
2016-09-04 16:08:11 +00:00
|
|
|
|
2017-03-26 02:42:32 +00:00
|
|
|
/* assume that add_submodule_odb() has been called */
|
|
|
|
refs = ref_store_init(submodule_sb.buf,
|
|
|
|
REF_STORE_READ | REF_STORE_ODB);
|
2017-04-04 10:21:20 +00:00
|
|
|
register_ref_store_map(&submodule_ref_stores, "submodule",
|
|
|
|
refs, submodule);
|
2017-03-26 02:42:31 +00:00
|
|
|
|
2017-08-23 12:36:53 +00:00
|
|
|
done:
|
2017-03-26 02:42:31 +00:00
|
|
|
strbuf_release(&submodule_sb);
|
2017-08-23 12:36:54 +00:00
|
|
|
free(to_free);
|
|
|
|
|
2016-09-04 16:08:11 +00:00
|
|
|
return refs;
|
|
|
|
}
|
|
|
|
|
2017-04-24 10:01:22 +00:00
|
|
|
struct ref_store *get_worktree_ref_store(const struct worktree *wt)
|
|
|
|
{
|
|
|
|
struct ref_store *refs;
|
|
|
|
const char *id;
|
|
|
|
|
|
|
|
if (wt->is_current)
|
2018-04-12 00:21:09 +00:00
|
|
|
return get_main_ref_store(the_repository);
|
2017-04-24 10:01:22 +00:00
|
|
|
|
|
|
|
id = wt->id ? wt->id : "/";
|
|
|
|
refs = lookup_ref_store_map(&worktree_ref_stores, id);
|
|
|
|
if (refs)
|
|
|
|
return refs;
|
|
|
|
|
|
|
|
if (wt->id)
|
|
|
|
refs = ref_store_init(git_common_path("worktrees/%s", wt->id),
|
|
|
|
REF_STORE_ALL_CAPS);
|
|
|
|
else
|
|
|
|
refs = ref_store_init(get_git_common_dir(),
|
|
|
|
REF_STORE_ALL_CAPS);
|
|
|
|
|
|
|
|
if (refs)
|
|
|
|
register_ref_store_map(&worktree_ref_stores, "worktree",
|
|
|
|
refs, id);
|
|
|
|
return refs;
|
|
|
|
}
|
|
|
|
|
2017-02-10 11:16:11 +00:00
|
|
|
void base_ref_store_init(struct ref_store *refs,
|
2017-02-10 11:16:17 +00:00
|
|
|
const struct ref_storage_be *be)
|
2016-09-04 16:08:11 +00:00
|
|
|
{
|
2017-02-10 11:16:11 +00:00
|
|
|
refs->be = be;
|
2016-09-04 16:08:11 +00:00
|
|
|
}
|
2016-09-04 16:08:16 +00:00
|
|
|
|
|
|
|
/* backend functions */
|
2017-03-26 02:42:34 +00:00
|
|
|
int refs_pack_refs(struct ref_store *refs, unsigned int flags)
|
2016-09-04 16:08:27 +00:00
|
|
|
{
|
|
|
|
return refs->be->pack_refs(refs, flags);
|
|
|
|
}
|
|
|
|
|
2017-03-26 02:42:34 +00:00
|
|
|
int refs_peel_ref(struct ref_store *refs, const char *refname,
|
2017-10-15 22:07:02 +00:00
|
|
|
struct object_id *oid)
|
2017-03-26 02:42:34 +00:00
|
|
|
{
|
2017-09-25 08:00:14 +00:00
|
|
|
int flag;
|
2017-10-15 22:06:56 +00:00
|
|
|
struct object_id base;
|
2017-09-25 08:00:14 +00:00
|
|
|
|
|
|
|
if (current_ref_iter && current_ref_iter->refname == refname) {
|
|
|
|
struct object_id peeled;
|
|
|
|
|
|
|
|
if (ref_iterator_peel(current_ref_iter, &peeled))
|
|
|
|
return -1;
|
2017-10-15 22:07:02 +00:00
|
|
|
oidcpy(oid, &peeled);
|
2017-09-25 08:00:14 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (refs_read_ref_full(refs, refname,
|
2017-10-15 22:06:56 +00:00
|
|
|
RESOLVE_REF_READING, &base, &flag))
|
2017-09-25 08:00:14 +00:00
|
|
|
return -1;
|
|
|
|
|
2017-10-15 22:07:10 +00:00
|
|
|
return peel_object(&base, oid);
|
2016-09-04 16:08:27 +00:00
|
|
|
}
|
|
|
|
|
2017-10-15 22:07:02 +00:00
|
|
|
int peel_ref(const char *refname, struct object_id *oid)
|
2016-09-04 16:08:29 +00:00
|
|
|
{
|
2018-04-12 00:21:09 +00:00
|
|
|
return refs_peel_ref(get_main_ref_store(the_repository), refname, oid);
|
2017-03-26 02:42:34 +00:00
|
|
|
}
|
2016-09-04 16:08:29 +00:00
|
|
|
|
2017-03-26 02:42:34 +00:00
|
|
|
int refs_create_symref(struct ref_store *refs,
|
|
|
|
const char *ref_target,
|
|
|
|
const char *refs_heads_master,
|
|
|
|
const char *logmsg)
|
|
|
|
{
|
|
|
|
return refs->be->create_symref(refs, ref_target,
|
|
|
|
refs_heads_master,
|
|
|
|
logmsg);
|
2016-09-04 16:08:29 +00:00
|
|
|
}
|
|
|
|
|
2016-09-04 16:08:28 +00:00
|
|
|
int create_symref(const char *ref_target, const char *refs_heads_master,
|
|
|
|
const char *logmsg)
|
|
|
|
{
|
2018-04-12 00:21:09 +00:00
|
|
|
return refs_create_symref(get_main_ref_store(the_repository), ref_target,
|
2017-03-26 02:42:34 +00:00
|
|
|
refs_heads_master, logmsg);
|
2016-09-04 16:08:28 +00:00
|
|
|
}
|
|
|
|
|
2017-05-22 14:17:45 +00:00
|
|
|
int ref_update_reject_duplicates(struct string_list *refnames,
|
|
|
|
struct strbuf *err)
|
|
|
|
{
|
2017-05-22 14:17:46 +00:00
|
|
|
size_t i, n = refnames->nr;
|
2017-05-22 14:17:45 +00:00
|
|
|
|
|
|
|
assert(err);
|
|
|
|
|
2017-05-22 14:17:47 +00:00
|
|
|
for (i = 1; i < n; i++) {
|
|
|
|
int cmp = strcmp(refnames->items[i - 1].string,
|
|
|
|
refnames->items[i].string);
|
|
|
|
|
|
|
|
if (!cmp) {
|
2017-05-22 14:17:45 +00:00
|
|
|
strbuf_addf(err,
|
2018-07-21 07:49:35 +00:00
|
|
|
_("multiple updates for ref '%s' not allowed"),
|
2017-05-22 14:17:45 +00:00
|
|
|
refnames->items[i].string);
|
|
|
|
return 1;
|
2017-05-22 14:17:47 +00:00
|
|
|
} else if (cmp > 0) {
|
2018-05-02 09:38:39 +00:00
|
|
|
BUG("ref_update_reject_duplicates() received unsorted list");
|
2017-05-22 14:17:45 +00:00
|
|
|
}
|
2017-05-22 14:17:47 +00:00
|
|
|
}
|
2017-05-22 14:17:45 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
ref_transaction_prepare(): new optional step for reference updates
In the future, compound reference stores will sometimes need to modify
references in two different reference stores at the same time, meaning
that a single logical reference transaction might have to be
implemented as two internal sub-transactions. They won't want to call
`ref_transaction_commit()` for the two sub-transactions one after the
other, because that wouldn't be atomic (the first commit could succeed
and the second one fail). Instead, they will want to prepare both
sub-transactions (i.e., obtain any necessary locks and do any
pre-checks), and only if both prepare steps succeed, then commit both
sub-transactions.
Start preparing for that day by adding a new, optional
`ref_transaction_prepare()` step to the reference transaction
sequence, which obtains the locks and does any prechecks, reporting
any errors that occur. Also add a `ref_transaction_abort()` function
that can be used to abort a sub-transaction even if it has already
been prepared.
That is on the side of the public-facing API. On the side of the
`ref_store` VTABLE, get rid of `transaction_commit` and instead add
methods `transaction_prepare`, `transaction_finish`, and
`transaction_abort`. A `ref_transaction_commit()` now basically calls
methods `transaction_prepare` then `transaction_finish`.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-05-22 14:17:44 +00:00
|
|
|
int ref_transaction_prepare(struct ref_transaction *transaction,
|
|
|
|
struct strbuf *err)
|
2016-09-04 16:08:16 +00:00
|
|
|
{
|
2017-03-26 02:42:35 +00:00
|
|
|
struct ref_store *refs = transaction->ref_store;
|
2016-09-04 16:08:16 +00:00
|
|
|
|
2017-05-22 14:17:43 +00:00
|
|
|
switch (transaction->state) {
|
|
|
|
case REF_TRANSACTION_OPEN:
|
|
|
|
/* Good. */
|
|
|
|
break;
|
ref_transaction_prepare(): new optional step for reference updates
In the future, compound reference stores will sometimes need to modify
references in two different reference stores at the same time, meaning
that a single logical reference transaction might have to be
implemented as two internal sub-transactions. They won't want to call
`ref_transaction_commit()` for the two sub-transactions one after the
other, because that wouldn't be atomic (the first commit could succeed
and the second one fail). Instead, they will want to prepare both
sub-transactions (i.e., obtain any necessary locks and do any
pre-checks), and only if both prepare steps succeed, then commit both
sub-transactions.
Start preparing for that day by adding a new, optional
`ref_transaction_prepare()` step to the reference transaction
sequence, which obtains the locks and does any prechecks, reporting
any errors that occur. Also add a `ref_transaction_abort()` function
that can be used to abort a sub-transaction even if it has already
been prepared.
That is on the side of the public-facing API. On the side of the
`ref_store` VTABLE, get rid of `transaction_commit` and instead add
methods `transaction_prepare`, `transaction_finish`, and
`transaction_abort`. A `ref_transaction_commit()` now basically calls
methods `transaction_prepare` then `transaction_finish`.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-05-22 14:17:44 +00:00
|
|
|
case REF_TRANSACTION_PREPARED:
|
2018-05-02 09:38:39 +00:00
|
|
|
BUG("prepare called twice on reference transaction");
|
ref_transaction_prepare(): new optional step for reference updates
In the future, compound reference stores will sometimes need to modify
references in two different reference stores at the same time, meaning
that a single logical reference transaction might have to be
implemented as two internal sub-transactions. They won't want to call
`ref_transaction_commit()` for the two sub-transactions one after the
other, because that wouldn't be atomic (the first commit could succeed
and the second one fail). Instead, they will want to prepare both
sub-transactions (i.e., obtain any necessary locks and do any
pre-checks), and only if both prepare steps succeed, then commit both
sub-transactions.
Start preparing for that day by adding a new, optional
`ref_transaction_prepare()` step to the reference transaction
sequence, which obtains the locks and does any prechecks, reporting
any errors that occur. Also add a `ref_transaction_abort()` function
that can be used to abort a sub-transaction even if it has already
been prepared.
That is on the side of the public-facing API. On the side of the
`ref_store` VTABLE, get rid of `transaction_commit` and instead add
methods `transaction_prepare`, `transaction_finish`, and
`transaction_abort`. A `ref_transaction_commit()` now basically calls
methods `transaction_prepare` then `transaction_finish`.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-05-22 14:17:44 +00:00
|
|
|
break;
|
2017-05-22 14:17:43 +00:00
|
|
|
case REF_TRANSACTION_CLOSED:
|
2018-05-02 09:38:39 +00:00
|
|
|
BUG("prepare called on a closed reference transaction");
|
2017-05-22 14:17:43 +00:00
|
|
|
break;
|
|
|
|
default:
|
2018-05-02 09:38:39 +00:00
|
|
|
BUG("unexpected reference transaction state");
|
2017-05-22 14:17:43 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-04-10 22:14:12 +00:00
|
|
|
if (getenv(GIT_QUARANTINE_ENVIRONMENT)) {
|
|
|
|
strbuf_addstr(err,
|
|
|
|
_("ref updates forbidden inside quarantine environment"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
ref_transaction_prepare(): new optional step for reference updates
In the future, compound reference stores will sometimes need to modify
references in two different reference stores at the same time, meaning
that a single logical reference transaction might have to be
implemented as two internal sub-transactions. They won't want to call
`ref_transaction_commit()` for the two sub-transactions one after the
other, because that wouldn't be atomic (the first commit could succeed
and the second one fail). Instead, they will want to prepare both
sub-transactions (i.e., obtain any necessary locks and do any
pre-checks), and only if both prepare steps succeed, then commit both
sub-transactions.
Start preparing for that day by adding a new, optional
`ref_transaction_prepare()` step to the reference transaction
sequence, which obtains the locks and does any prechecks, reporting
any errors that occur. Also add a `ref_transaction_abort()` function
that can be used to abort a sub-transaction even if it has already
been prepared.
That is on the side of the public-facing API. On the side of the
`ref_store` VTABLE, get rid of `transaction_commit` and instead add
methods `transaction_prepare`, `transaction_finish`, and
`transaction_abort`. A `ref_transaction_commit()` now basically calls
methods `transaction_prepare` then `transaction_finish`.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-05-22 14:17:44 +00:00
|
|
|
return refs->be->transaction_prepare(refs, transaction, err);
|
|
|
|
}
|
|
|
|
|
|
|
|
int ref_transaction_abort(struct ref_transaction *transaction,
|
|
|
|
struct strbuf *err)
|
|
|
|
{
|
|
|
|
struct ref_store *refs = transaction->ref_store;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
switch (transaction->state) {
|
|
|
|
case REF_TRANSACTION_OPEN:
|
|
|
|
/* No need to abort explicitly. */
|
|
|
|
break;
|
|
|
|
case REF_TRANSACTION_PREPARED:
|
|
|
|
ret = refs->be->transaction_abort(refs, transaction, err);
|
|
|
|
break;
|
|
|
|
case REF_TRANSACTION_CLOSED:
|
2018-05-02 09:38:39 +00:00
|
|
|
BUG("abort called on a closed reference transaction");
|
ref_transaction_prepare(): new optional step for reference updates
In the future, compound reference stores will sometimes need to modify
references in two different reference stores at the same time, meaning
that a single logical reference transaction might have to be
implemented as two internal sub-transactions. They won't want to call
`ref_transaction_commit()` for the two sub-transactions one after the
other, because that wouldn't be atomic (the first commit could succeed
and the second one fail). Instead, they will want to prepare both
sub-transactions (i.e., obtain any necessary locks and do any
pre-checks), and only if both prepare steps succeed, then commit both
sub-transactions.
Start preparing for that day by adding a new, optional
`ref_transaction_prepare()` step to the reference transaction
sequence, which obtains the locks and does any prechecks, reporting
any errors that occur. Also add a `ref_transaction_abort()` function
that can be used to abort a sub-transaction even if it has already
been prepared.
That is on the side of the public-facing API. On the side of the
`ref_store` VTABLE, get rid of `transaction_commit` and instead add
methods `transaction_prepare`, `transaction_finish`, and
`transaction_abort`. A `ref_transaction_commit()` now basically calls
methods `transaction_prepare` then `transaction_finish`.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-05-22 14:17:44 +00:00
|
|
|
break;
|
|
|
|
default:
|
2018-05-02 09:38:39 +00:00
|
|
|
BUG("unexpected reference transaction state");
|
ref_transaction_prepare(): new optional step for reference updates
In the future, compound reference stores will sometimes need to modify
references in two different reference stores at the same time, meaning
that a single logical reference transaction might have to be
implemented as two internal sub-transactions. They won't want to call
`ref_transaction_commit()` for the two sub-transactions one after the
other, because that wouldn't be atomic (the first commit could succeed
and the second one fail). Instead, they will want to prepare both
sub-transactions (i.e., obtain any necessary locks and do any
pre-checks), and only if both prepare steps succeed, then commit both
sub-transactions.
Start preparing for that day by adding a new, optional
`ref_transaction_prepare()` step to the reference transaction
sequence, which obtains the locks and does any prechecks, reporting
any errors that occur. Also add a `ref_transaction_abort()` function
that can be used to abort a sub-transaction even if it has already
been prepared.
That is on the side of the public-facing API. On the side of the
`ref_store` VTABLE, get rid of `transaction_commit` and instead add
methods `transaction_prepare`, `transaction_finish`, and
`transaction_abort`. A `ref_transaction_commit()` now basically calls
methods `transaction_prepare` then `transaction_finish`.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-05-22 14:17:44 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
ref_transaction_free(transaction);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ref_transaction_commit(struct ref_transaction *transaction,
|
|
|
|
struct strbuf *err)
|
|
|
|
{
|
|
|
|
struct ref_store *refs = transaction->ref_store;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
switch (transaction->state) {
|
|
|
|
case REF_TRANSACTION_OPEN:
|
|
|
|
/* Need to prepare first. */
|
|
|
|
ret = ref_transaction_prepare(transaction, err);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
break;
|
|
|
|
case REF_TRANSACTION_PREPARED:
|
|
|
|
/* Fall through to finish. */
|
|
|
|
break;
|
|
|
|
case REF_TRANSACTION_CLOSED:
|
2018-05-02 09:38:39 +00:00
|
|
|
BUG("commit called on a closed reference transaction");
|
ref_transaction_prepare(): new optional step for reference updates
In the future, compound reference stores will sometimes need to modify
references in two different reference stores at the same time, meaning
that a single logical reference transaction might have to be
implemented as two internal sub-transactions. They won't want to call
`ref_transaction_commit()` for the two sub-transactions one after the
other, because that wouldn't be atomic (the first commit could succeed
and the second one fail). Instead, they will want to prepare both
sub-transactions (i.e., obtain any necessary locks and do any
pre-checks), and only if both prepare steps succeed, then commit both
sub-transactions.
Start preparing for that day by adding a new, optional
`ref_transaction_prepare()` step to the reference transaction
sequence, which obtains the locks and does any prechecks, reporting
any errors that occur. Also add a `ref_transaction_abort()` function
that can be used to abort a sub-transaction even if it has already
been prepared.
That is on the side of the public-facing API. On the side of the
`ref_store` VTABLE, get rid of `transaction_commit` and instead add
methods `transaction_prepare`, `transaction_finish`, and
`transaction_abort`. A `ref_transaction_commit()` now basically calls
methods `transaction_prepare` then `transaction_finish`.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-05-22 14:17:44 +00:00
|
|
|
break;
|
|
|
|
default:
|
2018-05-02 09:38:39 +00:00
|
|
|
BUG("unexpected reference transaction state");
|
ref_transaction_prepare(): new optional step for reference updates
In the future, compound reference stores will sometimes need to modify
references in two different reference stores at the same time, meaning
that a single logical reference transaction might have to be
implemented as two internal sub-transactions. They won't want to call
`ref_transaction_commit()` for the two sub-transactions one after the
other, because that wouldn't be atomic (the first commit could succeed
and the second one fail). Instead, they will want to prepare both
sub-transactions (i.e., obtain any necessary locks and do any
pre-checks), and only if both prepare steps succeed, then commit both
sub-transactions.
Start preparing for that day by adding a new, optional
`ref_transaction_prepare()` step to the reference transaction
sequence, which obtains the locks and does any prechecks, reporting
any errors that occur. Also add a `ref_transaction_abort()` function
that can be used to abort a sub-transaction even if it has already
been prepared.
That is on the side of the public-facing API. On the side of the
`ref_store` VTABLE, get rid of `transaction_commit` and instead add
methods `transaction_prepare`, `transaction_finish`, and
`transaction_abort`. A `ref_transaction_commit()` now basically calls
methods `transaction_prepare` then `transaction_finish`.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-05-22 14:17:44 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return refs->be->transaction_finish(refs, transaction, err);
|
2016-09-04 16:08:16 +00:00
|
|
|
}
|
2016-09-04 16:08:26 +00:00
|
|
|
|
2017-03-26 02:42:34 +00:00
|
|
|
int refs_verify_refname_available(struct ref_store *refs,
|
|
|
|
const char *refname,
|
2017-04-16 06:41:26 +00:00
|
|
|
const struct string_list *extras,
|
2017-03-26 02:42:34 +00:00
|
|
|
const struct string_list *skip,
|
|
|
|
struct strbuf *err)
|
2016-09-04 16:08:26 +00:00
|
|
|
{
|
2017-04-16 06:41:26 +00:00
|
|
|
const char *slash;
|
|
|
|
const char *extra_refname;
|
|
|
|
struct strbuf dirname = STRBUF_INIT;
|
|
|
|
struct strbuf referent = STRBUF_INIT;
|
|
|
|
struct object_id oid;
|
|
|
|
unsigned int type;
|
|
|
|
struct ref_iterator *iter;
|
|
|
|
int ok;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* For the sake of comments in this function, suppose that
|
|
|
|
* refname is "refs/foo/bar".
|
|
|
|
*/
|
|
|
|
|
|
|
|
assert(err);
|
|
|
|
|
|
|
|
strbuf_grow(&dirname, strlen(refname) + 1);
|
|
|
|
for (slash = strchr(refname, '/'); slash; slash = strchr(slash + 1, '/')) {
|
|
|
|
/* Expand dirname to the new prefix, not including the trailing slash: */
|
|
|
|
strbuf_add(&dirname, refname + dirname.len, slash - refname - dirname.len);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We are still at a leading dir of the refname (e.g.,
|
|
|
|
* "refs/foo"; if there is a reference with that name,
|
|
|
|
* it is a conflict, *unless* it is in skip.
|
|
|
|
*/
|
|
|
|
if (skip && string_list_has_string(skip, dirname.buf))
|
|
|
|
continue;
|
|
|
|
|
2017-10-15 22:07:11 +00:00
|
|
|
if (!refs_read_raw_ref(refs, dirname.buf, &oid, &referent, &type)) {
|
2018-07-21 07:49:35 +00:00
|
|
|
strbuf_addf(err, _("'%s' exists; cannot create '%s'"),
|
2017-04-16 06:41:26 +00:00
|
|
|
dirname.buf, refname);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (extras && string_list_has_string(extras, dirname.buf)) {
|
2018-07-21 07:49:35 +00:00
|
|
|
strbuf_addf(err, _("cannot process '%s' and '%s' at the same time"),
|
2017-04-16 06:41:26 +00:00
|
|
|
refname, dirname.buf);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We are at the leaf of our refname (e.g., "refs/foo/bar").
|
|
|
|
* There is no point in searching for a reference with that
|
|
|
|
* name, because a refname isn't considered to conflict with
|
|
|
|
* itself. But we still need to check for references whose
|
|
|
|
* names are in the "refs/foo/bar/" namespace, because they
|
|
|
|
* *do* conflict.
|
|
|
|
*/
|
|
|
|
strbuf_addstr(&dirname, refname + dirname.len);
|
|
|
|
strbuf_addch(&dirname, '/');
|
|
|
|
|
|
|
|
iter = refs_ref_iterator_begin(refs, dirname.buf, 0,
|
|
|
|
DO_FOR_EACH_INCLUDE_BROKEN);
|
|
|
|
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
|
|
|
|
if (skip &&
|
|
|
|
string_list_has_string(skip, iter->refname))
|
|
|
|
continue;
|
|
|
|
|
2018-07-21 07:49:35 +00:00
|
|
|
strbuf_addf(err, _("'%s' exists; cannot create '%s'"),
|
2017-04-16 06:41:26 +00:00
|
|
|
iter->refname, refname);
|
|
|
|
ref_iterator_abort(iter);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ok != ITER_DONE)
|
2018-05-02 09:38:39 +00:00
|
|
|
BUG("error while iterating over references");
|
2017-04-16 06:41:26 +00:00
|
|
|
|
|
|
|
extra_refname = find_descendant_ref(dirname.buf, extras, skip);
|
|
|
|
if (extra_refname)
|
2018-07-21 07:49:35 +00:00
|
|
|
strbuf_addf(err, _("cannot process '%s' and '%s' at the same time"),
|
2017-04-16 06:41:26 +00:00
|
|
|
refname, extra_refname);
|
|
|
|
else
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
strbuf_release(&referent);
|
|
|
|
strbuf_release(&dirname);
|
|
|
|
return ret;
|
2016-09-04 16:08:26 +00:00
|
|
|
}
|
2016-09-04 16:08:38 +00:00
|
|
|
|
2017-03-26 02:42:34 +00:00
|
|
|
int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data)
|
2016-09-04 16:08:38 +00:00
|
|
|
{
|
|
|
|
struct ref_iterator *iter;
|
2018-08-20 18:24:16 +00:00
|
|
|
struct do_for_each_ref_help hp = { fn, cb_data };
|
2016-09-04 16:08:38 +00:00
|
|
|
|
|
|
|
iter = refs->be->reflog_iterator_begin(refs);
|
|
|
|
|
2018-08-20 18:24:16 +00:00
|
|
|
return do_for_each_repo_ref_iterator(the_repository, iter,
|
|
|
|
do_for_each_ref_helper, &hp);
|
2016-09-04 16:08:38 +00:00
|
|
|
}
|
|
|
|
|
2017-03-26 02:42:34 +00:00
|
|
|
int for_each_reflog(each_ref_fn fn, void *cb_data)
|
2016-09-04 16:08:38 +00:00
|
|
|
{
|
2018-04-12 00:21:09 +00:00
|
|
|
return refs_for_each_reflog(get_main_ref_store(the_repository), fn, cb_data);
|
2017-03-26 02:42:34 +00:00
|
|
|
}
|
2016-09-04 16:08:38 +00:00
|
|
|
|
2017-03-26 02:42:34 +00:00
|
|
|
int refs_for_each_reflog_ent_reverse(struct ref_store *refs,
|
|
|
|
const char *refname,
|
|
|
|
each_reflog_ent_fn fn,
|
|
|
|
void *cb_data)
|
|
|
|
{
|
2016-09-04 16:08:38 +00:00
|
|
|
return refs->be->for_each_reflog_ent_reverse(refs, refname,
|
|
|
|
fn, cb_data);
|
|
|
|
}
|
|
|
|
|
2017-03-26 02:42:34 +00:00
|
|
|
int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn,
|
|
|
|
void *cb_data)
|
|
|
|
{
|
2018-04-12 00:21:09 +00:00
|
|
|
return refs_for_each_reflog_ent_reverse(get_main_ref_store(the_repository),
|
2017-03-26 02:42:34 +00:00
|
|
|
refname, fn, cb_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
int refs_for_each_reflog_ent(struct ref_store *refs, const char *refname,
|
|
|
|
each_reflog_ent_fn fn, void *cb_data)
|
|
|
|
{
|
|
|
|
return refs->be->for_each_reflog_ent(refs, refname, fn, cb_data);
|
|
|
|
}
|
|
|
|
|
2016-09-04 16:08:38 +00:00
|
|
|
int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn,
|
|
|
|
void *cb_data)
|
|
|
|
{
|
2018-04-12 00:21:09 +00:00
|
|
|
return refs_for_each_reflog_ent(get_main_ref_store(the_repository), refname,
|
2017-03-26 02:42:34 +00:00
|
|
|
fn, cb_data);
|
|
|
|
}
|
2016-09-04 16:08:38 +00:00
|
|
|
|
2017-03-26 02:42:34 +00:00
|
|
|
int refs_reflog_exists(struct ref_store *refs, const char *refname)
|
|
|
|
{
|
|
|
|
return refs->be->reflog_exists(refs, refname);
|
2016-09-04 16:08:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int reflog_exists(const char *refname)
|
|
|
|
{
|
2018-04-12 00:21:09 +00:00
|
|
|
return refs_reflog_exists(get_main_ref_store(the_repository), refname);
|
2017-03-26 02:42:34 +00:00
|
|
|
}
|
2016-09-04 16:08:38 +00:00
|
|
|
|
2017-03-26 02:42:34 +00:00
|
|
|
int refs_create_reflog(struct ref_store *refs, const char *refname,
|
|
|
|
int force_create, struct strbuf *err)
|
|
|
|
{
|
|
|
|
return refs->be->create_reflog(refs, refname, force_create, err);
|
2016-09-04 16:08:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int safe_create_reflog(const char *refname, int force_create,
|
|
|
|
struct strbuf *err)
|
|
|
|
{
|
2018-04-12 00:21:09 +00:00
|
|
|
return refs_create_reflog(get_main_ref_store(the_repository), refname,
|
2017-03-26 02:42:34 +00:00
|
|
|
force_create, err);
|
|
|
|
}
|
2016-09-04 16:08:38 +00:00
|
|
|
|
2017-03-26 02:42:34 +00:00
|
|
|
int refs_delete_reflog(struct ref_store *refs, const char *refname)
|
|
|
|
{
|
|
|
|
return refs->be->delete_reflog(refs, refname);
|
2016-09-04 16:08:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int delete_reflog(const char *refname)
|
|
|
|
{
|
2018-04-12 00:21:09 +00:00
|
|
|
return refs_delete_reflog(get_main_ref_store(the_repository), refname);
|
2017-03-26 02:42:34 +00:00
|
|
|
}
|
2016-09-04 16:08:38 +00:00
|
|
|
|
2017-03-26 02:42:34 +00:00
|
|
|
int refs_reflog_expire(struct ref_store *refs,
|
2017-10-15 22:07:04 +00:00
|
|
|
const char *refname, const struct object_id *oid,
|
2017-03-26 02:42:34 +00:00
|
|
|
unsigned int flags,
|
|
|
|
reflog_expiry_prepare_fn prepare_fn,
|
|
|
|
reflog_expiry_should_prune_fn should_prune_fn,
|
|
|
|
reflog_expiry_cleanup_fn cleanup_fn,
|
|
|
|
void *policy_cb_data)
|
|
|
|
{
|
2017-10-15 22:07:04 +00:00
|
|
|
return refs->be->reflog_expire(refs, refname, oid, flags,
|
2017-03-26 02:42:34 +00:00
|
|
|
prepare_fn, should_prune_fn,
|
|
|
|
cleanup_fn, policy_cb_data);
|
2016-09-04 16:08:38 +00:00
|
|
|
}
|
|
|
|
|
2017-10-15 22:07:04 +00:00
|
|
|
int reflog_expire(const char *refname, const struct object_id *oid,
|
2016-09-04 16:08:38 +00:00
|
|
|
unsigned int flags,
|
|
|
|
reflog_expiry_prepare_fn prepare_fn,
|
|
|
|
reflog_expiry_should_prune_fn should_prune_fn,
|
|
|
|
reflog_expiry_cleanup_fn cleanup_fn,
|
|
|
|
void *policy_cb_data)
|
|
|
|
{
|
2018-04-12 00:21:09 +00:00
|
|
|
return refs_reflog_expire(get_main_ref_store(the_repository),
|
2017-10-15 22:07:04 +00:00
|
|
|
refname, oid, flags,
|
2017-03-26 02:42:34 +00:00
|
|
|
prepare_fn, should_prune_fn,
|
|
|
|
cleanup_fn, policy_cb_data);
|
2016-09-04 16:08:38 +00:00
|
|
|
}
|
2016-09-04 16:08:39 +00:00
|
|
|
|
|
|
|
int initial_ref_transaction_commit(struct ref_transaction *transaction,
|
|
|
|
struct strbuf *err)
|
|
|
|
{
|
2017-03-26 02:42:35 +00:00
|
|
|
struct ref_store *refs = transaction->ref_store;
|
2016-09-04 16:08:39 +00:00
|
|
|
|
|
|
|
return refs->be->initial_transaction_commit(refs, transaction, err);
|
|
|
|
}
|
2016-09-04 16:08:40 +00:00
|
|
|
|
2017-05-22 14:17:38 +00:00
|
|
|
int refs_delete_refs(struct ref_store *refs, const char *msg,
|
|
|
|
struct string_list *refnames, unsigned int flags)
|
2016-09-04 16:08:40 +00:00
|
|
|
{
|
2017-05-22 14:17:38 +00:00
|
|
|
return refs->be->delete_refs(refs, msg, refnames, flags);
|
2016-09-04 16:08:40 +00:00
|
|
|
}
|
2016-09-04 16:08:42 +00:00
|
|
|
|
2017-05-22 14:17:38 +00:00
|
|
|
int delete_refs(const char *msg, struct string_list *refnames,
|
|
|
|
unsigned int flags)
|
2016-09-04 16:08:42 +00:00
|
|
|
{
|
2018-04-12 00:21:09 +00:00
|
|
|
return refs_delete_refs(get_main_ref_store(the_repository), msg, refnames, flags);
|
2017-03-26 02:42:34 +00:00
|
|
|
}
|
2016-09-04 16:08:42 +00:00
|
|
|
|
2017-03-26 02:42:34 +00:00
|
|
|
int refs_rename_ref(struct ref_store *refs, const char *oldref,
|
|
|
|
const char *newref, const char *logmsg)
|
|
|
|
{
|
2016-09-04 16:08:42 +00:00
|
|
|
return refs->be->rename_ref(refs, oldref, newref, logmsg);
|
|
|
|
}
|
2017-03-26 02:42:34 +00:00
|
|
|
|
|
|
|
int rename_ref(const char *oldref, const char *newref, const char *logmsg)
|
|
|
|
{
|
2018-04-12 00:21:09 +00:00
|
|
|
return refs_rename_ref(get_main_ref_store(the_repository), oldref, newref, logmsg);
|
2017-03-26 02:42:34 +00:00
|
|
|
}
|
branch: add a --copy (-c) option to go with --move (-m)
Add the ability to --copy a branch and its reflog and configuration,
this uses the same underlying machinery as the --move (-m) option
except the reflog and configuration is copied instead of being moved.
This is useful for e.g. copying a topic branch to a new version,
e.g. work to work-2 after submitting the work topic to the list, while
preserving all the tracking info and other configuration that goes
with the branch, and unlike --move keeping the other already-submitted
branch around for reference.
Like --move, when the source branch is the currently checked out
branch the HEAD is moved to the destination branch. In the case of
--move we don't really have a choice (other than remaining on a
detached HEAD) and in order to keep the functionality consistent, we
are doing it in similar way for --copy too.
The most common usage of this feature is expected to be moving to a
new topic branch which is a copy of the current one, in that case
moving to the target branch is what the user wants, and doesn't
unexpectedly behave differently than --move would.
One outstanding caveat of this implementation is that:
git checkout maint &&
git checkout master &&
git branch -c topic &&
git checkout -
Will check out 'maint' instead of 'master'. This is because the @{-N}
feature (or its -1 shorthand "-") relies on HEAD reflogs created by
the checkout command, so in this case we'll checkout maint instead of
master, as the user might expect. What to do about that is left to a
future change.
Helped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Sahil Dua <sahildua2305@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-06-18 21:19:16 +00:00
|
|
|
|
|
|
|
int refs_copy_existing_ref(struct ref_store *refs, const char *oldref,
|
|
|
|
const char *newref, const char *logmsg)
|
|
|
|
{
|
|
|
|
return refs->be->copy_ref(refs, oldref, newref, logmsg);
|
|
|
|
}
|
|
|
|
|
|
|
|
int copy_existing_ref(const char *oldref, const char *newref, const char *logmsg)
|
|
|
|
{
|
2018-04-12 00:21:09 +00:00
|
|
|
return refs_copy_existing_ref(get_main_ref_store(the_repository), oldref, newref, logmsg);
|
branch: add a --copy (-c) option to go with --move (-m)
Add the ability to --copy a branch and its reflog and configuration,
this uses the same underlying machinery as the --move (-m) option
except the reflog and configuration is copied instead of being moved.
This is useful for e.g. copying a topic branch to a new version,
e.g. work to work-2 after submitting the work topic to the list, while
preserving all the tracking info and other configuration that goes
with the branch, and unlike --move keeping the other already-submitted
branch around for reference.
Like --move, when the source branch is the currently checked out
branch the HEAD is moved to the destination branch. In the case of
--move we don't really have a choice (other than remaining on a
detached HEAD) and in order to keep the functionality consistent, we
are doing it in similar way for --copy too.
The most common usage of this feature is expected to be moving to a
new topic branch which is a copy of the current one, in that case
moving to the target branch is what the user wants, and doesn't
unexpectedly behave differently than --move would.
One outstanding caveat of this implementation is that:
git checkout maint &&
git checkout master &&
git branch -c topic &&
git checkout -
Will check out 'maint' instead of 'master'. This is because the @{-N}
feature (or its -1 shorthand "-") relies on HEAD reflogs created by
the checkout command, so in this case we'll checkout maint instead of
master, as the user might expect. What to do about that is left to a
future change.
Helped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Sahil Dua <sahildua2305@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-06-18 21:19:16 +00:00
|
|
|
}
|