summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--builtin-branch.c24
-rw-r--r--cache.h2
-rwxr-xr-xgit-checkout.sh16
-rw-r--r--path.c26
-rw-r--r--setup.c5
5 files changed, 50 insertions, 23 deletions
diff --git a/builtin-branch.c b/builtin-branch.c
index 487965b540..c760e188ea 100644
--- a/builtin-branch.c
+++ b/builtin-branch.c
@@ -308,7 +308,8 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev)
free_ref_list(&ref_list);
}
-static void create_branch(const char *name, const char *start,
+static void create_branch(const char *name, const char *start_name,
+ unsigned char *start_sha1,
int force, int reflog)
{
struct ref_lock *lock;
@@ -327,9 +328,14 @@ static void create_branch(const char *name, const char *start,
die("Cannot force update the current branch.");
}
- if (get_sha1(start, sha1) ||
- (commit = lookup_commit_reference(sha1)) == NULL)
- die("Not a valid branch point: '%s'.", start);
+ if (start_sha1)
+ /* detached HEAD */
+ hashcpy(sha1, start_sha1);
+ else if (get_sha1(start_name, sha1))
+ die("Not a valid object name: '%s'.", start_name);
+
+ if ((commit = lookup_commit_reference(sha1)) == NULL)
+ die("Not a valid branch point: '%s'.", start_name);
hashcpy(sha1, commit->object.sha1);
lock = lock_any_ref_for_update(ref, NULL);
@@ -338,7 +344,8 @@ static void create_branch(const char *name, const char *start,
if (reflog) {
log_all_ref_updates = 1;
- snprintf(msg, sizeof msg, "branch: Created from %s", start);
+ snprintf(msg, sizeof msg, "branch: Created from %s",
+ start_name);
}
if (write_ref_sha1(lock, sha1, msg) < 0)
@@ -350,6 +357,9 @@ static void rename_branch(const char *oldname, const char *newname, int force)
char oldref[PATH_MAX], newref[PATH_MAX], logmsg[PATH_MAX*2 + 100];
unsigned char sha1[20];
+ if (!oldname)
+ die("cannot rename the curren branch while not on any.");
+
if (snprintf(oldref, sizeof(oldref), "refs/heads/%s", oldname) > sizeof(oldref))
die("Old branchname too long");
@@ -474,9 +484,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
else if (rename && (i == argc - 2))
rename_branch(argv[i], argv[i + 1], force_rename);
else if (i == argc - 1)
- create_branch(argv[i], head, force_create, reflog);
+ create_branch(argv[i], head, head_sha1, force_create, reflog);
else if (i == argc - 2)
- create_branch(argv[i], argv[i + 1], force_create, reflog);
+ create_branch(argv[i], argv[i+1], NULL, force_create, reflog);
else
usage(builtin_branch_usage);
diff --git a/cache.h b/cache.h
index 36be64e386..5218548ccf 100644
--- a/cache.h
+++ b/cache.h
@@ -299,7 +299,7 @@ extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */
extern int read_ref(const char *filename, unsigned char *sha1);
extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *);
extern int create_symref(const char *ref, const char *refs_heads_master);
-extern int validate_symref(const char *ref);
+extern int validate_headref(const char *ref);
extern int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
extern int cache_name_compare(const char *name1, int len1, const char *name2, int len2);
diff --git a/git-checkout.sh b/git-checkout.sh
index 92ec069a3a..8e11ca4bc8 100755
--- a/git-checkout.sh
+++ b/git-checkout.sh
@@ -144,13 +144,19 @@ fi
# are switching to, then we'd better just be checking out
# what we already had
-[ -z "$branch$newbranch" ] &&
- [ "$new" != "$old" ] &&
- die "git checkout: provided reference cannot be checked out directly
-
- You need -b to associate a new branch with the wanted checkout. Example:
+if test -z "$branch$newbranch" && test "$new" != "$old"
+then
+ # NEEDSWORK: we would want to have this command here
+ # that allows us to detach the HEAD atomically.
+ # git update-ref --detach HEAD "$new"
+ rm -f "$GIT_DIR/HEAD"
+ echo "$new" >"$GIT_DIR/HEAD"
+ echo >&2 "WARNING: you are not on ANY branch anymore.
+If you meant to create a new branch from the commit, you need -b to
+associate a new branch with the wanted checkout. Example:
git checkout -b <new_branch_name> $arg
"
+fi
if [ "X$old" = X ]
then
diff --git a/path.c b/path.c
index 066f621955..94ddd7eafc 100644
--- a/path.c
+++ b/path.c
@@ -90,10 +90,11 @@ int git_mkstemp(char *path, size_t len, const char *template)
}
-int validate_symref(const char *path)
+int validate_headref(const char *path)
{
struct stat st;
char *buf, buffer[256];
+ unsigned char sha1[20];
int len, fd;
if (lstat(path, &st) < 0)
@@ -119,14 +120,23 @@ int validate_symref(const char *path)
/*
* Is it a symbolic ref?
*/
- if (len < 4 || memcmp("ref:", buffer, 4))
+ if (len < 4)
return -1;
- buf = buffer + 4;
- len -= 4;
- while (len && isspace(*buf))
- buf++, len--;
- if (len >= 5 && !memcmp("refs/", buf, 5))
+ if (!memcmp("ref:", buffer, 4)) {
+ buf = buffer + 4;
+ len -= 4;
+ while (len && isspace(*buf))
+ buf++, len--;
+ if (len >= 5 && !memcmp("refs/", buf, 5))
+ return 0;
+ }
+
+ /*
+ * Is this a detached HEAD?
+ */
+ if (!get_sha1_hex(buffer, sha1))
return 0;
+
return -1;
}
@@ -241,7 +251,7 @@ char *enter_repo(char *path, int strict)
return NULL;
if (access("objects", X_OK) == 0 && access("refs", X_OK) == 0 &&
- validate_symref("HEAD") == 0) {
+ validate_headref("HEAD") == 0) {
putenv("GIT_DIR=.");
check_repository_format();
return path;
diff --git a/setup.c b/setup.c
index 2ae57f7c94..cc97f9f5c1 100644
--- a/setup.c
+++ b/setup.c
@@ -138,7 +138,8 @@ const char **get_pathspec(const char *prefix, const char **pathspec)
* GIT_OBJECT_DIRECTORY environment variable
* - a refs/ directory
* - either a HEAD symlink or a HEAD file that is formatted as
- * a proper "ref:".
+ * a proper "ref:", or a regular file HEAD that has a properly
+ * formatted sha1 object name.
*/
static int is_git_directory(const char *suspect)
{
@@ -161,7 +162,7 @@ static int is_git_directory(const char *suspect)
return 0;
strcpy(path + len, "/HEAD");
- if (validate_symref(path))
+ if (validate_headref(path))
return 0;
return 1;