mirror of
https://github.com/git/git.git
synced 2026-03-13 18:33:25 +01:00
Merge branch 'jc/detached-head' into next
* jc/detached-head: git-checkout: fix branch name output from the command git-checkout: safety when coming back from the detached HEAD state. git-checkout: rewording comments regarding detached HEAD. git-checkout: do not warn detaching HEAD when it is already detached. Detached HEAD (experimental) git-branch: show detached HEAD git-status: show detached HEAD cvsimport: cleanup temporary cvsps file cvsimport: document -S and -L options cvsimport: skip commits that are too recent (option and documentation)
This commit is contained in:
@@ -90,7 +90,8 @@ If you need to pass multiple options, separate them with a comma.
|
||||
Print a short usage message and exit.
|
||||
|
||||
-z <fuzz>::
|
||||
Pass the timestamp fuzz factor to cvsps.
|
||||
Pass the timestamp fuzz factor to cvsps, in seconds. If unset,
|
||||
cvsps defaults to 300s.
|
||||
|
||||
-s <subst>::
|
||||
Substitute the character "/" in branch names with <subst>
|
||||
@@ -99,6 +100,18 @@ If you need to pass multiple options, separate them with a comma.
|
||||
CVS by default uses the unix username when writing its
|
||||
commit logs. Using this option and an author-conv-file
|
||||
in this format
|
||||
|
||||
-a::
|
||||
Import all commits, including recent ones. cvsimport by default
|
||||
skips commits that have a timestamp less than 10 minutes ago.
|
||||
|
||||
-S <regex>::
|
||||
Skip paths matching the regex.
|
||||
|
||||
-L <limit>::
|
||||
Limit the number of commits imported. Workaround for cases where
|
||||
cvsimport leaks memory.
|
||||
|
||||
+
|
||||
---------
|
||||
exon=Andreas Ericsson <ae@op5.se>
|
||||
|
||||
@@ -275,7 +275,7 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
|
||||
}
|
||||
}
|
||||
|
||||
static void print_ref_list(int kinds, int verbose, int abbrev)
|
||||
static void print_ref_list(int kinds, int detached, int verbose, int abbrev)
|
||||
{
|
||||
int i;
|
||||
struct ref_list ref_list;
|
||||
@@ -286,8 +286,20 @@ static void print_ref_list(int kinds, int verbose, int abbrev)
|
||||
|
||||
qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp);
|
||||
|
||||
detached = (detached && (kinds & REF_LOCAL_BRANCH));
|
||||
if (detached) {
|
||||
struct ref_item item;
|
||||
item.name = "(no branch)";
|
||||
item.kind = REF_LOCAL_BRANCH;
|
||||
hashcpy(item.sha1, head_sha1);
|
||||
if (strlen(item.name) > ref_list.maxwidth)
|
||||
ref_list.maxwidth = strlen(item.name);
|
||||
print_ref_item(&item, ref_list.maxwidth, verbose, abbrev, 1);
|
||||
}
|
||||
|
||||
for (i = 0; i < ref_list.index; i++) {
|
||||
int current = (ref_list.list[i].kind == REF_LOCAL_BRANCH) &&
|
||||
int current = !detached &&
|
||||
(ref_list.list[i].kind == REF_LOCAL_BRANCH) &&
|
||||
!strcmp(ref_list.list[i].name, head);
|
||||
print_ref_item(&ref_list.list[i], ref_list.maxwidth, verbose,
|
||||
abbrev, current);
|
||||
@@ -296,7 +308,8 @@ static void print_ref_list(int kinds, 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;
|
||||
@@ -315,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);
|
||||
@@ -326,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)
|
||||
@@ -338,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");
|
||||
|
||||
@@ -367,7 +389,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int delete = 0, force_delete = 0, force_create = 0;
|
||||
int rename = 0, force_rename = 0;
|
||||
int verbose = 0, abbrev = DEFAULT_ABBREV;
|
||||
int verbose = 0, abbrev = DEFAULT_ABBREV, detached = 0;
|
||||
int reflog = 0;
|
||||
int kinds = REF_LOCAL_BRANCH;
|
||||
int i;
|
||||
@@ -444,22 +466,27 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
|
||||
head = xstrdup(resolve_ref("HEAD", head_sha1, 0, NULL));
|
||||
if (!head)
|
||||
die("Failed to resolve HEAD as a valid ref.");
|
||||
if (strncmp(head, "refs/heads/", 11))
|
||||
die("HEAD not found below refs/heads!");
|
||||
head += 11;
|
||||
if (!strcmp(head, "HEAD")) {
|
||||
detached = 1;
|
||||
}
|
||||
else {
|
||||
if (strncmp(head, "refs/heads/", 11))
|
||||
die("HEAD not found below refs/heads!");
|
||||
head += 11;
|
||||
}
|
||||
|
||||
if (delete)
|
||||
return delete_branches(argc - i, argv + i, force_delete, kinds);
|
||||
else if (i == argc)
|
||||
print_ref_list(kinds, verbose, abbrev);
|
||||
print_ref_list(kinds, detached, verbose, abbrev);
|
||||
else if (rename && (i == argc - 1))
|
||||
rename_branch(head, argv[i], force_rename);
|
||||
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);
|
||||
|
||||
|
||||
2
cache.h
2
cache.h
@@ -300,7 +300,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);
|
||||
|
||||
@@ -6,6 +6,7 @@ SUBDIRECTORY_OK=Sometimes
|
||||
|
||||
old_name=HEAD
|
||||
old=$(git-rev-parse --verify $old_name 2>/dev/null)
|
||||
oldbranch=$(git-symbolic-ref $old_name 2>/dev/null)
|
||||
new=
|
||||
new_name=
|
||||
force=
|
||||
@@ -13,6 +14,8 @@ branch=
|
||||
newbranch=
|
||||
newbranch_log=
|
||||
merge=
|
||||
LF='
|
||||
'
|
||||
while [ "$#" != "0" ]; do
|
||||
arg="$1"
|
||||
shift
|
||||
@@ -50,7 +53,7 @@ while [ "$#" != "0" ]; do
|
||||
exit 1
|
||||
fi
|
||||
new="$rev"
|
||||
new_name="$arg^0"
|
||||
new_name="$arg"
|
||||
if git-show-ref --verify --quiet -- "refs/heads/$arg"
|
||||
then
|
||||
branch="$arg"
|
||||
@@ -139,23 +142,52 @@ fi
|
||||
|
||||
[ -z "$new" ] && new=$old && new_name="$old_name"
|
||||
|
||||
# If we don't have an old branch that we're switching to,
|
||||
# If we don't have an existing branch that we're switching to,
|
||||
# and we don't have a new branch name for the target we
|
||||
# are switching to, then we'd better just be checking out
|
||||
# what we already had
|
||||
# are switching to, then we are detaching our HEAD from any
|
||||
# branch. However, if "git checkout HEAD" detaches the HEAD
|
||||
# from the current branch, even though that may be logically
|
||||
# correct, it feels somewhat funny. More importantly, we do not
|
||||
# want "git checkout" nor "git checkout -f" to detach HEAD.
|
||||
|
||||
[ -z "$branch$newbranch" ] &&
|
||||
[ "$new" != "$old" ] &&
|
||||
die "git checkout: provided reference cannot be checked out directly
|
||||
if test -z "$branch$newbranch" && test "$new" != "$old"
|
||||
then
|
||||
# NEEDSWORK: we would want to have a command here
|
||||
# that allows us to detach the HEAD atomically. Perhaps
|
||||
# something like "git update-ref --detach HEAD $new"
|
||||
echo "$new" >"$GIT_DIR/HEAD.new" &&
|
||||
mv "$GIT_DIR/HEAD.new" "$GIT_DIR/HEAD" || die "Cannot detach HEAD"
|
||||
|
||||
You need -b to associate a new branch with the wanted checkout. Example:
|
||||
if test -n "$oldbranch"
|
||||
then
|
||||
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
|
||||
elif test -z "$oldbranch" && test -n "$branch"
|
||||
then
|
||||
# Coming back...
|
||||
if test -z "$force"
|
||||
then
|
||||
mb=$(git merge-base --all $old $new) &&
|
||||
case "$LF$mb$LF" in
|
||||
*"$LF$old$LF"*) : ;;
|
||||
*) false ;;
|
||||
esac || {
|
||||
echo >&2 \
|
||||
"You are not on a branch and switching to $new_name branch may lose
|
||||
your changes. Use 'git checkout -f $new_name' if you want to."
|
||||
exit 1;
|
||||
}
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "X$old" = X ]
|
||||
then
|
||||
echo "warning: You do not appear to currently be on a branch." >&2
|
||||
echo "warning: Forcing checkout of $new_name." >&2
|
||||
echo >&2 "warning: You appear to be on a branch yet to be born."
|
||||
echo >&2 "warning: Forcing checkout of $new_name."
|
||||
force=1
|
||||
fi
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ use IPC::Open2;
|
||||
$SIG{'PIPE'}="IGNORE";
|
||||
$ENV{'TZ'}="UTC";
|
||||
|
||||
our ($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,$opt_M,$opt_A,$opt_S,$opt_L);
|
||||
our ($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,$opt_M,$opt_A,$opt_S,$opt_L, $opt_a);
|
||||
my (%conv_author_name, %conv_author_email);
|
||||
|
||||
sub usage() {
|
||||
@@ -37,7 +37,7 @@ sub usage() {
|
||||
Usage: ${\basename $0} # fetch/update GIT from CVS
|
||||
[-o branch-for-HEAD] [-h] [-v] [-d CVSROOT] [-A author-conv-file]
|
||||
[-p opts-for-cvsps] [-C GIT_repository] [-z fuzz] [-i] [-k] [-u]
|
||||
[-s subst] [-m] [-M regex] [-S regex] [CVS_module]
|
||||
[-s subst] [-a] [-m] [-M regex] [-S regex] [CVS_module]
|
||||
END
|
||||
exit(1);
|
||||
}
|
||||
@@ -105,6 +105,8 @@ if ($opt_d) {
|
||||
}
|
||||
$opt_o ||= "origin";
|
||||
$opt_s ||= "-";
|
||||
$opt_a ||= 0;
|
||||
|
||||
my $git_tree = $opt_C;
|
||||
$git_tree ||= ".";
|
||||
|
||||
@@ -573,9 +575,11 @@ if ($opt_A) {
|
||||
# run cvsps into a file unless we are getting
|
||||
# it passed as a file via $opt_P
|
||||
#
|
||||
my $cvspsfile;
|
||||
unless ($opt_P) {
|
||||
print "Running cvsps...\n" if $opt_v;
|
||||
my $pid = open(CVSPS,"-|");
|
||||
my $cvspsfh;
|
||||
die "Cannot fork: $!\n" unless defined $pid;
|
||||
unless ($pid) {
|
||||
my @opt;
|
||||
@@ -588,18 +592,18 @@ unless ($opt_P) {
|
||||
exec("cvsps","--norc",@opt,"-u","-A",'--root',$opt_d,$cvs_tree);
|
||||
die "Could not start cvsps: $!\n";
|
||||
}
|
||||
my ($cvspsfh, $cvspsfile) = tempfile('gitXXXXXX', SUFFIX => '.cvsps',
|
||||
DIR => File::Spec->tmpdir());
|
||||
($cvspsfh, $cvspsfile) = tempfile('gitXXXXXX', SUFFIX => '.cvsps',
|
||||
DIR => File::Spec->tmpdir());
|
||||
while (<CVSPS>) {
|
||||
print $cvspsfh $_;
|
||||
}
|
||||
close CVSPS;
|
||||
close $cvspsfh;
|
||||
$opt_P = $cvspsfile;
|
||||
} else {
|
||||
$cvspsfile = $opt_P;
|
||||
}
|
||||
|
||||
|
||||
open(CVS, "<$opt_P") or die $!;
|
||||
open(CVS, "<$cvspsfile") or die $!;
|
||||
|
||||
## cvsps output:
|
||||
#---------------------
|
||||
@@ -829,7 +833,7 @@ while (<CVS>) {
|
||||
$state = 11;
|
||||
next;
|
||||
}
|
||||
if ( $starttime - 300 - (defined $opt_z ? $opt_z : 300) <= $date) {
|
||||
if (!$opt_a && $starttime - 300 - (defined $opt_z ? $opt_z : 300) <= $date) {
|
||||
# skip if the commit is too recent
|
||||
# that the cvsps default fuzz is 300s, we give ourselves another
|
||||
# 300s just in case -- this also prevents skipping commits
|
||||
@@ -934,6 +938,10 @@ while (<CVS>) {
|
||||
}
|
||||
commit() if $branch and $state != 11;
|
||||
|
||||
unless ($opt_P) {
|
||||
unlink($cvspsfile);
|
||||
}
|
||||
|
||||
# The heuristic of repacking every 1024 commits can leave a
|
||||
# lot of unpacked data. If there is more than 1MB worth of
|
||||
# not-packed objects, repack once more.
|
||||
|
||||
26
path.c
26
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;
|
||||
|
||||
5
setup.c
5
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;
|
||||
|
||||
@@ -48,7 +48,7 @@ test_expect_success \
|
||||
test ! -f .git/logs/refs/heads/d/e/f'
|
||||
|
||||
cat >expect <<EOF
|
||||
0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 checkout: Created from master^0
|
||||
0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 checkout: Created from master
|
||||
EOF
|
||||
test_expect_success \
|
||||
'git checkout -b g/h/i -l should create a branch and a log' \
|
||||
|
||||
13
wt-status.c
13
wt-status.c
@@ -288,9 +288,18 @@ void wt_status_print(struct wt_status *s)
|
||||
unsigned char sha1[20];
|
||||
s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0;
|
||||
|
||||
if (s->branch)
|
||||
if (s->branch) {
|
||||
const char *on_what = "On branch ";
|
||||
const char *branch_name = s->branch;
|
||||
if (!strncmp(branch_name, "refs/heads/", 11))
|
||||
branch_name += 11;
|
||||
else if (!strcmp(branch_name, "HEAD")) {
|
||||
branch_name = "";
|
||||
on_what = "Not currently on any branch.";
|
||||
}
|
||||
color_printf_ln(color(WT_STATUS_HEADER),
|
||||
"# On branch %s", s->branch);
|
||||
"# %s%s", on_what, branch_name);
|
||||
}
|
||||
|
||||
if (s->is_initial) {
|
||||
color_printf_ln(color(WT_STATUS_HEADER), "#");
|
||||
|
||||
Reference in New Issue
Block a user