Skip to content

Commit 5bebcd4

Browse files
committed
Merge branch 'em/checkout-orphan'
* em/checkout-orphan: log_ref_setup: don't return stack-allocated array bash completion: add --orphan to 'git checkout' t3200: test -l with core.logAllRefUpdates options checkout --orphan: respect -l option always refs: split log_ref_write logic into log_ref_setup Documentation: alter checkout --orphan description
2 parents 8c6b5a8 + 157aaea commit 5bebcd4

File tree

7 files changed

+168
-60
lines changed

7 files changed

+168
-60
lines changed

Documentation/git-checkout.txt

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -91,22 +91,29 @@ explicitly give a name with '-b' in such a case.
9191
details.
9292

9393
--orphan::
94-
Create a new branch named <new_branch>, unparented to any other
95-
branch. The new branch you switch to does not have any commit
96-
and after the first one it will become the root of a new history
97-
completely unconnected from all the other branches.
94+
Create a new 'orphan' branch, named <new_branch>, started from
95+
<start_point> and switch to it. The first commit made on this
96+
new branch will have no parents and it will be the root of a new
97+
history totally disconnected from all the other branches and
98+
commits.
9899
+
99-
When you use "--orphan", the index and the working tree are kept intact.
100-
This allows you to start a new history that records set of paths similar
101-
to that of the start-point commit, which is useful when you want to keep
102-
different branches for different audiences you are working to like when
103-
you have an open source and commercial versions of a software, for example.
100+
The index and the working tree are adjusted as if you had previously run
101+
"git checkout <start_point>". This allows you to start a new history
102+
that records a set of paths similar to <start_point> by easily running
103+
"git commit -a" to make the root commit.
104104
+
105-
If you want to start a disconnected history that records set of paths
106-
totally different from the original branch, you may want to first clear
107-
the index and the working tree, by running "git rm -rf ." from the
108-
top-level of the working tree, before preparing your files (by copying
109-
from elsewhere, extracting a tarball, etc.) in the working tree.
105+
This can be useful when you want to publish the tree from a commit
106+
without exposing its full history. You might want to do this to publish
107+
an open source branch of a project whose current tree is "clean", but
108+
whose full history contains proprietary or otherwise encumbered bits of
109+
code.
110+
+
111+
If you want to start a disconnected history that records a set of paths
112+
that is totally different from the one of <start_point>, then you should
113+
clear the index and the working tree right after creating the orphan
114+
branch by running "git rm -rf ." from the top level of the working tree.
115+
Afterwards you will be ready to prepare your new files, repopulating the
116+
working tree, by copying them from elsewhere, extracting a tarball, etc.
110117

111118
-m::
112119
--merge::

builtin/checkout.c

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,24 @@ static void update_refs_for_switch(struct checkout_opts *opts,
493493
struct strbuf msg = STRBUF_INIT;
494494
const char *old_desc;
495495
if (opts->new_branch) {
496-
if (!opts->new_orphan_branch)
496+
if (opts->new_orphan_branch) {
497+
if (opts->new_branch_log && !log_all_ref_updates) {
498+
int temp;
499+
char log_file[PATH_MAX];
500+
char *ref_name = mkpath("refs/heads/%s", opts->new_orphan_branch);
501+
502+
temp = log_all_ref_updates;
503+
log_all_ref_updates = 1;
504+
if (log_ref_setup(ref_name, log_file, sizeof(log_file))) {
505+
fprintf(stderr, "Can not do reflog for '%s'\n",
506+
opts->new_orphan_branch);
507+
log_all_ref_updates = temp;
508+
return;
509+
}
510+
log_all_ref_updates = temp;
511+
}
512+
}
513+
else
497514
create_branch(old->name, opts->new_branch, new->name, 0,
498515
opts->new_branch_log, opts->track);
499516
new->name = opts->new_branch;
@@ -517,6 +534,14 @@ static void update_refs_for_switch(struct checkout_opts *opts,
517534
opts->new_branch ? " a new" : "",
518535
new->name);
519536
}
537+
if (old->path && old->name) {
538+
char log_file[PATH_MAX], ref_file[PATH_MAX];
539+
540+
git_snpath(log_file, sizeof(log_file), "logs/%s", old->path);
541+
git_snpath(ref_file, sizeof(ref_file), "%s", old->path);
542+
if (!file_exists(ref_file) && file_exists(log_file))
543+
remove_path(log_file);
544+
}
520545
} else if (strcmp(new->name, "HEAD")) {
521546
update_ref(msg.buf, "HEAD", new->commit->object.sha1, NULL,
522547
REF_NODEREF, DIE_ON_ERR);
@@ -684,8 +709,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
684709
if (opts.new_orphan_branch) {
685710
if (opts.new_branch)
686711
die("--orphan and -b are mutually exclusive");
687-
if (opts.track > 0 || opts.new_branch_log)
688-
die("--orphan cannot be used with -t or -l");
712+
if (opts.track > 0)
713+
die("--orphan cannot be used with -t");
689714
opts.new_branch = opts.new_orphan_branch;
690715
}
691716

contrib/completion/git-completion.bash

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -842,7 +842,7 @@ _git_checkout ()
842842
--*)
843843
__gitcomp "
844844
--quiet --ours --theirs --track --no-track --merge
845-
--conflict= --patch
845+
--conflict= --orphan --patch
846846
"
847847
;;
848848
*)

refs.c

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1258,52 +1258,65 @@ static int copy_msg(char *buf, const char *msg)
12581258
return cp - buf;
12591259
}
12601260

1261-
static int log_ref_write(const char *ref_name, const unsigned char *old_sha1,
1262-
const unsigned char *new_sha1, const char *msg)
1261+
int log_ref_setup(const char *ref_name, char *logfile, int bufsize)
12631262
{
1264-
int logfd, written, oflags = O_APPEND | O_WRONLY;
1265-
unsigned maxlen, len;
1266-
int msglen;
1267-
char log_file[PATH_MAX];
1268-
char *logrec;
1269-
const char *committer;
1270-
1271-
if (log_all_ref_updates < 0)
1272-
log_all_ref_updates = !is_bare_repository();
1273-
1274-
git_snpath(log_file, sizeof(log_file), "logs/%s", ref_name);
1263+
int logfd, oflags = O_APPEND | O_WRONLY;
12751264

1265+
git_snpath(logfile, bufsize, "logs/%s", ref_name);
12761266
if (log_all_ref_updates &&
12771267
(!prefixcmp(ref_name, "refs/heads/") ||
12781268
!prefixcmp(ref_name, "refs/remotes/") ||
12791269
!prefixcmp(ref_name, "refs/notes/") ||
12801270
!strcmp(ref_name, "HEAD"))) {
1281-
if (safe_create_leading_directories(log_file) < 0)
1271+
if (safe_create_leading_directories(logfile) < 0)
12821272
return error("unable to create directory for %s",
1283-
log_file);
1273+
logfile);
12841274
oflags |= O_CREAT;
12851275
}
12861276

1287-
logfd = open(log_file, oflags, 0666);
1277+
logfd = open(logfile, oflags, 0666);
12881278
if (logfd < 0) {
12891279
if (!(oflags & O_CREAT) && errno == ENOENT)
12901280
return 0;
12911281

12921282
if ((oflags & O_CREAT) && errno == EISDIR) {
1293-
if (remove_empty_directories(log_file)) {
1283+
if (remove_empty_directories(logfile)) {
12941284
return error("There are still logs under '%s'",
1295-
log_file);
1285+
logfile);
12961286
}
1297-
logfd = open(log_file, oflags, 0666);
1287+
logfd = open(logfile, oflags, 0666);
12981288
}
12991289

13001290
if (logfd < 0)
13011291
return error("Unable to append to %s: %s",
1302-
log_file, strerror(errno));
1292+
logfile, strerror(errno));
13031293
}
13041294

1305-
adjust_shared_perm(log_file);
1295+
adjust_shared_perm(logfile);
1296+
close(logfd);
1297+
return 0;
1298+
}
13061299

1300+
static int log_ref_write(const char *ref_name, const unsigned char *old_sha1,
1301+
const unsigned char *new_sha1, const char *msg)
1302+
{
1303+
int logfd, result, written, oflags = O_APPEND | O_WRONLY;
1304+
unsigned maxlen, len;
1305+
int msglen;
1306+
char log_file[PATH_MAX];
1307+
char *logrec;
1308+
const char *committer;
1309+
1310+
if (log_all_ref_updates < 0)
1311+
log_all_ref_updates = !is_bare_repository();
1312+
1313+
result = log_ref_setup(ref_name, log_file, sizeof(log_file));
1314+
if (result)
1315+
return result;
1316+
1317+
logfd = open(log_file, oflags);
1318+
if (logfd < 0)
1319+
return 0;
13071320
msglen = msg ? strlen(msg) : 0;
13081321
committer = git_committer_info(0);
13091322
maxlen = strlen(committer) + msglen + 100;

refs.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ extern void unlock_ref(struct ref_lock *lock);
6868
/** Writes sha1 into the ref specified by the lock. **/
6969
extern int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1, const char *msg);
7070

71+
/** Setup reflog before using. **/
72+
int log_ref_setup(const char *ref_name, char *logfile, int bufsize);
73+
7174
/** Reads log for the value of ref during at_time. **/
7275
extern int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *sha1, char **msg, unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt);
7376

t/t2017-checkout-orphan.sh

Lines changed: 57 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,62 @@ test_expect_success '--orphan must be rejected with -b' '
4949
test refs/heads/master = "$(git symbolic-ref HEAD)"
5050
'
5151

52+
test_expect_success '--orphan must be rejected with -t' '
53+
git checkout master &&
54+
test_must_fail git checkout --orphan new -t master &&
55+
test refs/heads/master = "$(git symbolic-ref HEAD)"
56+
'
57+
58+
test_expect_success '--orphan ignores branch.autosetupmerge' '
59+
git checkout master &&
60+
git config branch.autosetupmerge always &&
61+
git checkout --orphan gamma &&
62+
test -z "$(git config branch.gamma.merge)" &&
63+
test refs/heads/gamma = "$(git symbolic-ref HEAD)" &&
64+
test_must_fail git rev-parse --verify HEAD^
65+
'
66+
67+
test_expect_success '--orphan makes reflog by default' '
68+
git checkout master &&
69+
git config --unset core.logAllRefUpdates &&
70+
git checkout --orphan delta &&
71+
! test -f .git/logs/refs/heads/delta &&
72+
test_must_fail PAGER= git reflog show delta &&
73+
git commit -m Delta &&
74+
test -f .git/logs/refs/heads/delta &&
75+
PAGER= git reflog show delta
76+
'
77+
78+
test_expect_success '--orphan does not make reflog when core.logAllRefUpdates = false' '
79+
git checkout master &&
80+
git config core.logAllRefUpdates false &&
81+
git checkout --orphan epsilon &&
82+
! test -f .git/logs/refs/heads/epsilon &&
83+
test_must_fail PAGER= git reflog show epsilon &&
84+
git commit -m Epsilon &&
85+
! test -f .git/logs/refs/heads/epsilon &&
86+
test_must_fail PAGER= git reflog show epsilon
87+
'
88+
89+
test_expect_success '--orphan with -l makes reflog when core.logAllRefUpdates = false' '
90+
git checkout master &&
91+
git checkout -l --orphan zeta &&
92+
test -f .git/logs/refs/heads/zeta &&
93+
test_must_fail PAGER= git reflog show zeta &&
94+
git commit -m Zeta &&
95+
PAGER= git reflog show zeta
96+
'
97+
98+
test_expect_success 'giving up --orphan not committed when -l and core.logAllRefUpdates = false deletes reflog' '
99+
git checkout master &&
100+
git checkout -l --orphan eta &&
101+
test -f .git/logs/refs/heads/eta &&
102+
test_must_fail PAGER= git reflog show eta &&
103+
git checkout master &&
104+
! test -f .git/logs/refs/heads/eta &&
105+
test_must_fail PAGER= git reflog show eta
106+
'
107+
52108
test_expect_success '--orphan is rejected with an existing name' '
53109
git checkout master &&
54110
test_must_fail git checkout --orphan master &&
@@ -60,31 +116,11 @@ test_expect_success '--orphan refuses to switch if a merge is needed' '
60116
git reset --hard &&
61117
echo local >>"$TEST_FILE" &&
62118
cat "$TEST_FILE" >"$TEST_FILE.saved" &&
63-
test_must_fail git checkout --orphan gamma master^ &&
119+
test_must_fail git checkout --orphan new master^ &&
64120
test refs/heads/master = "$(git symbolic-ref HEAD)" &&
65121
test_cmp "$TEST_FILE" "$TEST_FILE.saved" &&
66122
git diff-index --quiet --cached HEAD &&
67123
git reset --hard
68124
'
69125

70-
test_expect_success '--orphan does not mix well with -t' '
71-
git checkout master &&
72-
test_must_fail git checkout -t master --orphan gamma &&
73-
test refs/heads/master = "$(git symbolic-ref HEAD)"
74-
'
75-
76-
test_expect_success '--orphan ignores branch.autosetupmerge' '
77-
git checkout -f master &&
78-
git config branch.autosetupmerge always &&
79-
git checkout --orphan delta &&
80-
test -z "$(git config branch.delta.merge)" &&
81-
test refs/heads/delta = "$(git symbolic-ref HEAD)" &&
82-
test_must_fail git rev-parse --verify HEAD^
83-
'
84-
85-
test_expect_success '--orphan does not mix well with -l' '
86-
git checkout -f master &&
87-
test_must_fail git checkout -l --orphan gamma
88-
'
89-
90126
test_done

t/t3200-branch.sh

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,30 @@ test_expect_success \
224224
test -f .git/logs/refs/heads/g/h/i &&
225225
diff expect .git/logs/refs/heads/g/h/i'
226226

227+
test_expect_success 'checkout -b makes reflog by default' '
228+
git checkout master &&
229+
git config --unset core.logAllRefUpdates &&
230+
git checkout -b alpha &&
231+
test -f .git/logs/refs/heads/alpha &&
232+
PAGER= git reflog show alpha
233+
'
234+
235+
test_expect_success 'checkout -b does not make reflog when core.logAllRefUpdates = false' '
236+
git checkout master &&
237+
git config core.logAllRefUpdates false &&
238+
git checkout -b beta &&
239+
! test -f .git/logs/refs/heads/beta &&
240+
test_must_fail PAGER= git reflog show beta
241+
'
242+
243+
test_expect_success 'checkout -b with -l makes reflog when core.logAllRefUpdates = false' '
244+
git checkout master &&
245+
git checkout -lb gamma &&
246+
git config --unset core.logAllRefUpdates &&
247+
test -f .git/logs/refs/heads/gamma &&
248+
PAGER= git reflog show gamma
249+
'
250+
227251
test_expect_success 'avoid ambiguous track' '
228252
git config branch.autosetupmerge true &&
229253
git config remote.ambi1.url lalala &&

0 commit comments

Comments
 (0)