summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--builtin-apply.c4
-rw-r--r--builtin-branch.c2
-rw-r--r--builtin-config.c2
-rw-r--r--builtin-reflog.c4
-rw-r--r--builtin-remote.c4
-rw-r--r--builtin-reset.c2
-rw-r--r--builtin-revert.c2
-rw-r--r--builtin-send-pack.c2
-rw-r--r--builtin-tag.c4
-rw-r--r--builtin-update-ref.c8
-rw-r--r--cache.h9
-rw-r--r--config.c6
-rw-r--r--environment.c2
-rw-r--r--path.c54
-rw-r--r--perl/Git.pm3
-rw-r--r--receive-pack.c2
-rw-r--r--refs.c48
-rw-r--r--rerere.c2
-rw-r--r--server-info.c2
-rw-r--r--sha1_name.c6
-rwxr-xr-xt/t1400-update-ref.sh18
-rwxr-xr-xt/t3200-branch.sh9
22 files changed, 150 insertions, 45 deletions
diff --git a/builtin-apply.c b/builtin-apply.c
index e9d49f133a..50b623e54c 100644
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -2850,8 +2850,8 @@ static void create_one_file(char *path, unsigned mode, const char *buf, unsigned
unsigned int nr = getpid();
for (;;) {
- const char *newpath;
- newpath = mkpath("%s~%u", path, nr);
+ char newpath[PATH_MAX];
+ mksnpath(newpath, sizeof(newpath), "%s~%u", path, nr);
if (!try_create_file(newpath, mode, buf, size)) {
if (!rename(newpath, path))
return;
diff --git a/builtin-branch.c b/builtin-branch.c
index b1a2ad7a6b..4b4abfd363 100644
--- a/builtin-branch.c
+++ b/builtin-branch.c
@@ -160,7 +160,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
continue;
}
- if (delete_ref(name, sha1)) {
+ if (delete_ref(name, sha1, 0)) {
error("Error deleting %sbranch '%s'", remote,
argv[i]);
ret = 1;
diff --git a/builtin-config.c b/builtin-config.c
index 91fdc4985d..f71016204b 100644
--- a/builtin-config.c
+++ b/builtin-config.c
@@ -84,7 +84,7 @@ static int get_value(const char* key_, const char* regex_)
local = config_exclusive_filename;
if (!local) {
const char *home = getenv("HOME");
- local = repo_config = xstrdup(git_path("config"));
+ local = repo_config = git_pathdup("config");
if (git_config_global() && home)
global = xstrdup(mkpath("%s/.gitconfig", home));
if (git_config_system())
diff --git a/builtin-reflog.c b/builtin-reflog.c
index 196fa03b7f..da96da317b 100644
--- a/builtin-reflog.c
+++ b/builtin-reflog.c
@@ -277,11 +277,11 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
lock = lock_any_ref_for_update(ref, sha1, 0);
if (!lock)
return error("cannot lock ref '%s'", ref);
- log_file = xstrdup(git_path("logs/%s", ref));
+ log_file = git_pathdup("logs/%s", ref);
if (!file_exists(log_file))
goto finish;
if (!cmd->dry_run) {
- newlog_path = xstrdup(git_path("logs/%s.lock", ref));
+ newlog_path = git_pathdup("logs/%s.lock", ref);
cb.newlog = fopen(newlog_path, "w");
}
diff --git a/builtin-remote.c b/builtin-remote.c
index 90a4e35828..584280fbf5 100644
--- a/builtin-remote.c
+++ b/builtin-remote.c
@@ -340,7 +340,7 @@ static int remove_branches(struct string_list *branches)
const char *refname = item->string;
unsigned char *sha1 = item->util;
- if (delete_ref(refname, sha1))
+ if (delete_ref(refname, sha1, 0))
result |= error("Could not remove branch %s", refname);
}
return result;
@@ -570,7 +570,7 @@ static int prune(int argc, const char **argv)
const char *refname = states.stale.items[i].util;
if (!dry_run)
- result |= delete_ref(refname, NULL);
+ result |= delete_ref(refname, NULL, 0);
printf(" * [%s] %s\n", dry_run ? "would prune" : "pruned",
abbrev_ref(refname, "refs/remotes/"));
diff --git a/builtin-reset.c b/builtin-reset.c
index 16e6bb20f1..9514b77f8c 100644
--- a/builtin-reset.c
+++ b/builtin-reset.c
@@ -279,7 +279,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
update_ref(msg, "ORIG_HEAD", orig, old_orig, 0, MSG_ON_ERR);
}
else if (old_orig)
- delete_ref("ORIG_HEAD", old_orig);
+ delete_ref("ORIG_HEAD", old_orig, 0);
prepend_reflog_action("updating HEAD", msg, sizeof(msg));
update_ref_status = update_ref(msg, "HEAD", sha1, orig, 0, MSG_ON_ERR);
diff --git a/builtin-revert.c b/builtin-revert.c
index 786a956f26..74845ef8e6 100644
--- a/builtin-revert.c
+++ b/builtin-revert.c
@@ -269,7 +269,7 @@ static int revert_or_cherry_pick(int argc, const char **argv)
int i;
char *oneline, *reencoded_message = NULL;
const char *message, *encoding;
- char *defmsg = xstrdup(git_path("MERGE_MSG"));
+ char *defmsg = git_pathdup("MERGE_MSG");
git_config(git_default_config, NULL);
me = action == REVERT ? "revert" : "cherry-pick";
diff --git a/builtin-send-pack.c b/builtin-send-pack.c
index 8fdeaa3989..e428eace2b 100644
--- a/builtin-send-pack.c
+++ b/builtin-send-pack.c
@@ -232,7 +232,7 @@ static void update_tracking_ref(struct remote *remote, struct ref *ref)
if (args.verbose)
fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst);
if (ref->deletion) {
- delete_ref(rs.dst, NULL);
+ delete_ref(rs.dst, NULL, 0);
} else
update_ref("update by push", rs.dst,
ref->new_sha1, NULL, 0, 0);
diff --git a/builtin-tag.c b/builtin-tag.c
index f2853d08c7..843e9ac056 100644
--- a/builtin-tag.c
+++ b/builtin-tag.c
@@ -125,7 +125,7 @@ static int for_each_tag_name(const char **argv, each_tag_name_fn fn)
static int delete_tag(const char *name, const char *ref,
const unsigned char *sha1)
{
- if (delete_ref(ref, sha1))
+ if (delete_ref(ref, sha1, 0))
return 1;
printf("Deleted tag '%s'\n", name);
return 0;
@@ -283,7 +283,7 @@ static void create_tag(const unsigned char *object, const char *tag,
int fd;
/* write the template message before editing: */
- path = xstrdup(git_path("TAG_EDITMSG"));
+ path = git_pathdup("TAG_EDITMSG");
fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
if (fd < 0)
die("could not create file '%s': %s",
diff --git a/builtin-update-ref.c b/builtin-update-ref.c
index 56a0b1b39c..378dc1b7a6 100644
--- a/builtin-update-ref.c
+++ b/builtin-update-ref.c
@@ -13,7 +13,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
{
const char *refname, *oldval, *msg=NULL;
unsigned char sha1[20], oldsha1[20];
- int delete = 0, no_deref = 0;
+ int delete = 0, no_deref = 0, flags = 0;
struct option options[] = {
OPT_STRING( 'm', NULL, &msg, "reason", "reason of the update"),
OPT_BOOLEAN('d', NULL, &delete, "deletes the reference"),
@@ -47,9 +47,11 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
if (oldval && *oldval && get_sha1(oldval, oldsha1))
die("%s: not a valid old SHA1", oldval);
+ if (no_deref)
+ flags = REF_NODEREF;
if (delete)
- return delete_ref(refname, oldval ? oldsha1 : NULL);
+ return delete_ref(refname, oldval ? oldsha1 : NULL, flags);
else
return update_ref(msg, refname, sha1, oldval ? oldsha1 : NULL,
- no_deref ? REF_NODEREF : 0, DIE_ON_ERR);
+ flags, DIE_ON_ERR);
}
diff --git a/cache.h b/cache.h
index 74339edaaa..a1e4982cd4 100644
--- a/cache.h
+++ b/cache.h
@@ -424,7 +424,7 @@ extern int commit_locked_index(struct lock_file *);
extern void set_alternate_index_output(const char *);
extern int close_lock_file(struct lock_file *);
extern void rollback_lock_file(struct lock_file *);
-extern int delete_ref(const char *, const unsigned char *sha1);
+extern int delete_ref(const char *, const unsigned char *sha1, int delopt);
/* Environment bits from configuration mechanism */
extern int trust_executable_bit;
@@ -484,6 +484,13 @@ extern int check_repository_format(void);
#define DATA_CHANGED 0x0020
#define TYPE_CHANGED 0x0040
+extern char *mksnpath(char *buf, size_t n, const char *fmt, ...)
+ __attribute__((format (printf, 3, 4)));
+extern char *git_snpath(char *buf, size_t n, const char *fmt, ...)
+ __attribute__((format (printf, 3, 4)));
+extern char *git_pathdup(const char *fmt, ...)
+ __attribute__((format (printf, 1, 2)));
+
/* Return a statically allocated filename matching the sha1 signature */
extern char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
extern char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
diff --git a/config.c b/config.c
index 53f04a076a..82807c83b2 100644
--- a/config.c
+++ b/config.c
@@ -630,7 +630,7 @@ int git_config(config_fn_t fn, void *data)
free(user_config);
}
- repo_config = xstrdup(git_path("config"));
+ repo_config = git_pathdup("config");
ret += git_config_from_file(fn, repo_config, data);
free(repo_config);
return ret;
@@ -872,7 +872,7 @@ int git_config_set_multivar(const char* key, const char* value,
if (config_exclusive_filename)
config_filename = xstrdup(config_exclusive_filename);
else
- config_filename = xstrdup(git_path("config"));
+ config_filename = git_pathdup("config");
/*
* Since "key" actually contains the section name and the real
@@ -1132,7 +1132,7 @@ int git_config_rename_section(const char *old_name, const char *new_name)
if (config_exclusive_filename)
config_filename = xstrdup(config_exclusive_filename);
else
- config_filename = xstrdup(git_path("config"));
+ config_filename = git_pathdup("config");
out_fd = hold_lock_file_for_update(lock, config_filename, 0);
if (out_fd < 0) {
ret = error("could not lock config file %s", config_filename);
diff --git a/environment.c b/environment.c
index 0c6d11f6a0..df4f03a95f 100644
--- a/environment.c
+++ b/environment.c
@@ -71,7 +71,7 @@ static void setup_git_env(void)
}
git_graft_file = getenv(GRAFT_ENVIRONMENT);
if (!git_graft_file)
- git_graft_file = xstrdup(git_path("info/grafts"));
+ git_graft_file = git_pathdup("info/grafts");
}
int is_bare_repository(void)
diff --git a/path.c b/path.c
index c1cb54b7b8..eb24017535 100644
--- a/path.c
+++ b/path.c
@@ -32,6 +32,60 @@ static char *cleanup_path(char *path)
return path;
}
+char *mksnpath(char *buf, size_t n, const char *fmt, ...)
+{
+ va_list args;
+ unsigned len;
+
+ va_start(args, fmt);
+ len = vsnprintf(buf, n, fmt, args);
+ va_end(args);
+ if (len >= n) {
+ snprintf(buf, n, bad_path);
+ return buf;
+ }
+ return cleanup_path(buf);
+}
+
+static char *git_vsnpath(char *buf, size_t n, const char *fmt, va_list args)
+{
+ const char *git_dir = get_git_dir();
+ size_t len;
+
+ len = strlen(git_dir);
+ if (n < len + 1)
+ goto bad;
+ memcpy(buf, git_dir, len);
+ if (len && !is_dir_sep(git_dir[len-1]))
+ buf[len++] = '/';
+ len += vsnprintf(buf + len, n - len, fmt, args);
+ if (len >= n)
+ goto bad;
+ return cleanup_path(buf);
+bad:
+ snprintf(buf, n, bad_path);
+ return buf;
+}
+
+char *git_snpath(char *buf, size_t n, const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ (void)git_vsnpath(buf, n, fmt, args);
+ va_end(args);
+ return buf;
+}
+
+char *git_pathdup(const char *fmt, ...)
+{
+ char path[PATH_MAX];
+ va_list args;
+ va_start(args, fmt);
+ (void)git_vsnpath(path, sizeof(path), fmt, args);
+ va_end(args);
+ return xstrdup(path);
+}
+
char *mkpath(const char *fmt, ...)
{
va_list args;
diff --git a/perl/Git.pm b/perl/Git.pm
index 6aab712e6a..ba94453781 100644
--- a/perl/Git.pm
+++ b/perl/Git.pm
@@ -1203,8 +1203,7 @@ either version 2, or (at your option) any later version.
# the method was called upon an instance and (undef, @args) if
# it was called directly.
sub _maybe_self {
- # This breaks inheritance. Oh well.
- ref $_[0] eq 'Git' ? @_ : (undef, @_);
+ UNIVERSAL::isa($_[0], 'Git') ? @_ : (undef, @_);
}
# Check if the command id is something reasonable.
diff --git a/receive-pack.c b/receive-pack.c
index d44c19e6b5..f0145bd901 100644
--- a/receive-pack.c
+++ b/receive-pack.c
@@ -222,7 +222,7 @@ static const char *update(struct command *cmd)
warning ("Allowing deletion of corrupt ref.");
old_sha1 = NULL;
}
- if (delete_ref(name, old_sha1)) {
+ if (delete_ref(name, old_sha1, 0)) {
error("failed to delete %s", name);
return "failed to delete";
}
diff --git a/refs.c b/refs.c
index 7d91b670c5..293389e764 100644
--- a/refs.c
+++ b/refs.c
@@ -401,7 +401,7 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *
*flag = 0;
for (;;) {
- const char *path = git_path("%s", ref);
+ char path[PATH_MAX];
struct stat st;
char *buf;
int fd;
@@ -409,6 +409,7 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *
if (--depth < 0)
return NULL;
+ git_snpath(path, sizeof(path), "%s", ref);
/* Special case: non-existing file.
* Not having the refs/heads/new-branch is OK
* if we are writing into it, so is .git/HEAD
@@ -916,25 +917,33 @@ static int repack_without_ref(const char *refname)
return commit_lock_file(&packlock);
}
-int delete_ref(const char *refname, const unsigned char *sha1)
+int delete_ref(const char *refname, const unsigned char *sha1, int delopt)
{
struct ref_lock *lock;
- int err, i, ret = 0, flag = 0;
+ int err, i = 0, ret = 0, flag = 0;
lock = lock_ref_sha1_basic(refname, sha1, 0, &flag);
if (!lock)
return 1;
- if (!(flag & REF_ISPACKED)) {
+ if (!(flag & REF_ISPACKED) || flag & REF_ISSYMREF) {
/* loose */
- i = strlen(lock->lk->filename) - 5; /* .lock */
- lock->lk->filename[i] = 0;
- err = unlink(lock->lk->filename);
+ const char *path;
+
+ if (!(delopt & REF_NODEREF)) {
+ i = strlen(lock->lk->filename) - 5; /* .lock */
+ lock->lk->filename[i] = 0;
+ path = lock->lk->filename;
+ } else {
+ path = git_path(refname);
+ }
+ err = unlink(path);
if (err && errno != ENOENT) {
ret = 1;
error("unlink(%s) failed: %s",
- lock->lk->filename, strerror(errno));
+ path, strerror(errno));
}
- lock->lk->filename[i] = '.';
+ if (!(delopt & REF_NODEREF))
+ lock->lk->filename[i] = '.';
}
/* removing the loose one could have resurrected an earlier
* packed one. Also, if it was not loose we need to repack
@@ -959,11 +968,16 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
struct ref_lock *lock;
struct stat loginfo;
int log = !lstat(git_path("logs/%s", oldref), &loginfo);
+ const char *symref = NULL;
- if (S_ISLNK(loginfo.st_mode))
+ if (log && S_ISLNK(loginfo.st_mode))
return error("reflog for %s is a symlink", oldref);
- if (!resolve_ref(oldref, orig_sha1, 1, &flag))
+ symref = resolve_ref(oldref, orig_sha1, 1, &flag);
+ if (flag & REF_ISSYMREF)
+ return error("refname %s is a symbolic ref, renaming it is not supported",
+ oldref);
+ if (!symref)
return error("refname %s not found", oldref);
if (!is_refname_available(newref, oldref, get_packed_refs(), 0))
@@ -983,12 +997,12 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
return error("unable to move logfile logs/%s to tmp-renamed-log: %s",
oldref, strerror(errno));
- if (delete_ref(oldref, orig_sha1)) {
+ if (delete_ref(oldref, orig_sha1, REF_NODEREF)) {
error("unable to delete old %s", oldref);
goto rollback;
}
- if (resolve_ref(newref, sha1, 1, &flag) && delete_ref(newref, sha1)) {
+ if (resolve_ref(newref, sha1, 1, &flag) && delete_ref(newref, sha1, REF_NODEREF)) {
if (errno==EISDIR) {
if (remove_empty_directories(git_path("%s", newref))) {
error("Directory not empty: %s", newref);
@@ -1031,7 +1045,6 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
error("unable to lock %s for update", newref);
goto rollback;
}
-
lock->force_write = 1;
hashcpy(lock->old_sha1, orig_sha1);
if (write_ref_sha1(lock, orig_sha1, logmsg)) {
@@ -1125,13 +1138,14 @@ static int log_ref_write(const char *ref_name, const unsigned char *old_sha1,
int logfd, written, oflags = O_APPEND | O_WRONLY;
unsigned maxlen, len;
int msglen;
- char *log_file, *logrec;
+ char log_file[PATH_MAX];
+ char *logrec;
const char *committer;
if (log_all_ref_updates < 0)
log_all_ref_updates = !is_bare_repository();
- log_file = git_path("logs/%s", ref_name);
+ git_snpath(log_file, sizeof(log_file), "logs/%s", ref_name);
if (log_all_ref_updates &&
(!prefixcmp(ref_name, "refs/heads/") ||
@@ -1260,7 +1274,7 @@ int create_symref(const char *ref_target, const char *refs_heads_master,
const char *lockpath;
char ref[1000];
int fd, len, written;
- char *git_HEAD = xstrdup(git_path("%s", ref_target));
+ char *git_HEAD = git_pathdup("%s", ref_target);
unsigned char old_sha1[20], new_sha1[20];
if (logmsg && read_ref(ref_target, old_sha1))
diff --git a/rerere.c b/rerere.c
index 2b7a99d729..a0d477aaca 100644
--- a/rerere.c
+++ b/rerere.c
@@ -345,7 +345,7 @@ int setup_rerere(struct string_list *merge_rr)
if (!is_rerere_enabled())
return -1;
- merge_rr_path = xstrdup(git_path("MERGE_RR"));
+ merge_rr_path = git_pathdup("MERGE_RR");
fd = hold_lock_file_for_update(&write_lock, merge_rr_path,
LOCK_DIE_ON_ERROR);
read_rr(merge_rr);
diff --git a/server-info.c b/server-info.c
index c1c073b2f0..66b0d9d878 100644
--- a/server-info.c
+++ b/server-info.c
@@ -25,7 +25,7 @@ static int add_info_ref(const char *path, const unsigned char *sha1, int flag, v
static int update_info_refs(int force)
{
- char *path0 = xstrdup(git_path("info/refs"));
+ char *path0 = git_pathdup("info/refs");
int len = strlen(path0);
char *path1 = xmalloc(len + 2);
diff --git a/sha1_name.c b/sha1_name.c
index 4fb77f8863..75a5a7e96f 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -245,11 +245,13 @@ int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)
*ref = NULL;
for (p = ref_rev_parse_rules; *p; p++) {
+ char fullref[PATH_MAX];
unsigned char sha1_from_ref[20];
unsigned char *this_result;
this_result = refs_found ? sha1_from_ref : sha1;
- r = resolve_ref(mkpath(*p, len, str), this_result, 1, NULL);
+ mksnpath(fullref, sizeof(fullref), *p, len, str);
+ r = resolve_ref(fullref, this_result, 1, NULL);
if (r) {
if (!refs_found++)
*ref = xstrdup(r);
@@ -272,7 +274,7 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
char path[PATH_MAX];
const char *ref, *it;
- strcpy(path, mkpath(*p, len, str));
+ mksnpath(path, sizeof(path), *p, len, str);
ref = resolve_ref(path, hash, 1, NULL);
if (!ref)
continue;
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 04c2b164bc..bd589268fc 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -75,6 +75,24 @@ test_expect_success "delete $m (by HEAD)" '
'
rm -f .git/$m
+cp -f .git/HEAD .git/HEAD.orig
+test_expect_success "delete symref without dereference" '
+ git update-ref --no-deref -d HEAD &&
+ ! test -f .git/HEAD
+'
+cp -f .git/HEAD.orig .git/HEAD
+
+test_expect_success "delete symref without dereference when the referred ref is packed" '
+ echo foo >foo.c &&
+ git add foo.c &&
+ git commit -m foo &&
+ git pack-refs --all &&
+ git update-ref --no-deref -d HEAD &&
+ ! test -f .git/HEAD
+'
+cp -f .git/HEAD.orig .git/HEAD
+git update-ref -d $m
+
test_expect_success '(not) create HEAD with old sha1' "
test_must_fail git update-ref HEAD $A $B
"
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index 2147eacc50..25e9971fd8 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -112,6 +112,15 @@ test_expect_success 'config information was renamed, too' \
"test $(git config branch.s.dummy) = Hello &&
test_must_fail git config branch.s/s/dummy"
+test_expect_success 'renaming a symref is not allowed' \
+'
+ git symbolic-ref refs/heads/master2 refs/heads/master &&
+ test_must_fail git branch -m master2 master3 &&
+ git symbolic-ref refs/heads/master2 &&
+ test -f .git/refs/heads/master &&
+ ! test -f .git/refs/heads/master3
+'
+
test_expect_success \
'git branch -m u v should fail when the reflog for u is a symlink' '
git branch -l u &&