Daniel Barkalow | 782c2d6 | 2008-02-07 11:40:23 -0500 | [diff] [blame] | 1 | #include "cache.h" |
| 2 | #include "builtin.h" |
| 3 | #include "parse-options.h" |
| 4 | #include "refs.h" |
| 5 | #include "commit.h" |
| 6 | #include "tree.h" |
| 7 | #include "tree-walk.h" |
| 8 | #include "unpack-trees.h" |
| 9 | #include "dir.h" |
| 10 | #include "run-command.h" |
| 11 | #include "merge-recursive.h" |
| 12 | #include "branch.h" |
| 13 | #include "diff.h" |
| 14 | #include "revision.h" |
Junio C Hamano | 79a1e6b | 2008-02-16 17:17:09 -0800 | [diff] [blame] | 15 | #include "remote.h" |
Daniel Barkalow | 782c2d6 | 2008-02-07 11:40:23 -0500 | [diff] [blame] | 16 | |
| 17 | static const char * const checkout_usage[] = { |
| 18 | "git checkout [options] <branch>", |
| 19 | "git checkout [options] [<branch>] -- <file>...", |
| 20 | NULL, |
| 21 | }; |
| 22 | |
| 23 | static int post_checkout_hook(struct commit *old, struct commit *new, |
| 24 | int changed) |
| 25 | { |
| 26 | struct child_process proc; |
| 27 | const char *name = git_path("hooks/post-checkout"); |
| 28 | const char *argv[5]; |
| 29 | |
| 30 | if (access(name, X_OK) < 0) |
| 31 | return 0; |
| 32 | |
| 33 | memset(&proc, 0, sizeof(proc)); |
| 34 | argv[0] = name; |
| 35 | argv[1] = xstrdup(sha1_to_hex(old->object.sha1)); |
| 36 | argv[2] = xstrdup(sha1_to_hex(new->object.sha1)); |
| 37 | argv[3] = changed ? "1" : "0"; |
| 38 | argv[4] = NULL; |
| 39 | proc.argv = argv; |
| 40 | proc.no_stdin = 1; |
| 41 | proc.stdout_to_stderr = 1; |
| 42 | return run_command(&proc); |
| 43 | } |
| 44 | |
| 45 | static int update_some(const unsigned char *sha1, const char *base, int baselen, |
| 46 | const char *pathname, unsigned mode, int stage) |
| 47 | { |
| 48 | int len; |
| 49 | struct cache_entry *ce; |
| 50 | |
| 51 | if (S_ISGITLINK(mode)) |
| 52 | return 0; |
| 53 | |
| 54 | if (S_ISDIR(mode)) |
| 55 | return READ_TREE_RECURSIVE; |
| 56 | |
| 57 | len = baselen + strlen(pathname); |
| 58 | ce = xcalloc(1, cache_entry_size(len)); |
| 59 | hashcpy(ce->sha1, sha1); |
| 60 | memcpy(ce->name, base, baselen); |
| 61 | memcpy(ce->name + baselen, pathname, len - baselen); |
| 62 | ce->ce_flags = create_ce_flags(len, 0); |
| 63 | ce->ce_mode = create_ce_mode(mode); |
| 64 | add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE); |
| 65 | return 0; |
| 66 | } |
| 67 | |
| 68 | static int read_tree_some(struct tree *tree, const char **pathspec) |
| 69 | { |
Daniel Barkalow | 782c2d6 | 2008-02-07 11:40:23 -0500 | [diff] [blame] | 70 | read_tree_recursive(tree, "", 0, 0, pathspec, update_some); |
| 71 | |
Daniel Barkalow | 782c2d6 | 2008-02-07 11:40:23 -0500 | [diff] [blame] | 72 | /* update the index with the given tree's info |
| 73 | * for all args, expanding wildcards, and exit |
| 74 | * with any non-zero return code. |
| 75 | */ |
| 76 | return 0; |
| 77 | } |
| 78 | |
Daniel Barkalow | 7533687 | 2008-02-28 16:52:44 -0500 | [diff] [blame] | 79 | static int checkout_paths(struct tree *source_tree, const char **pathspec) |
Daniel Barkalow | 782c2d6 | 2008-02-07 11:40:23 -0500 | [diff] [blame] | 80 | { |
| 81 | int pos; |
| 82 | struct checkout state; |
| 83 | static char *ps_matched; |
| 84 | unsigned char rev[20]; |
| 85 | int flag; |
| 86 | struct commit *head; |
| 87 | |
Daniel Barkalow | 7533687 | 2008-02-28 16:52:44 -0500 | [diff] [blame] | 88 | int newfd; |
| 89 | struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file)); |
| 90 | |
| 91 | newfd = hold_locked_index(lock_file, 1); |
| 92 | read_cache(); |
| 93 | |
| 94 | if (source_tree) |
| 95 | read_tree_some(source_tree, pathspec); |
| 96 | |
Daniel Barkalow | 782c2d6 | 2008-02-07 11:40:23 -0500 | [diff] [blame] | 97 | for (pos = 0; pathspec[pos]; pos++) |
| 98 | ; |
| 99 | ps_matched = xcalloc(1, pos); |
| 100 | |
| 101 | for (pos = 0; pos < active_nr; pos++) { |
| 102 | struct cache_entry *ce = active_cache[pos]; |
| 103 | pathspec_match(pathspec, ps_matched, ce->name, 0); |
| 104 | } |
| 105 | |
| 106 | if (report_path_error(ps_matched, pathspec, 0)) |
| 107 | return 1; |
| 108 | |
| 109 | memset(&state, 0, sizeof(state)); |
| 110 | state.force = 1; |
| 111 | state.refresh_cache = 1; |
| 112 | for (pos = 0; pos < active_nr; pos++) { |
| 113 | struct cache_entry *ce = active_cache[pos]; |
| 114 | if (pathspec_match(pathspec, NULL, ce->name, 0)) { |
| 115 | checkout_entry(ce, &state, NULL); |
| 116 | } |
| 117 | } |
| 118 | |
Daniel Barkalow | 7533687 | 2008-02-28 16:52:44 -0500 | [diff] [blame] | 119 | if (write_cache(newfd, active_cache, active_nr) || |
| 120 | commit_locked_index(lock_file)) |
| 121 | die("unable to write new index file"); |
| 122 | |
Daniel Barkalow | 782c2d6 | 2008-02-07 11:40:23 -0500 | [diff] [blame] | 123 | resolve_ref("HEAD", rev, 0, &flag); |
| 124 | head = lookup_commit_reference_gently(rev, 1); |
| 125 | |
| 126 | return post_checkout_hook(head, head, 0); |
| 127 | } |
| 128 | |
| 129 | static void show_local_changes(struct object *head) |
| 130 | { |
| 131 | struct rev_info rev; |
| 132 | /* I think we want full paths, even if we're in a subdirectory. */ |
| 133 | init_revisions(&rev, NULL); |
| 134 | rev.abbrev = 0; |
| 135 | rev.diffopt.output_format |= DIFF_FORMAT_NAME_STATUS; |
| 136 | add_pending_object(&rev, head, NULL); |
| 137 | run_diff_index(&rev, 0); |
| 138 | } |
| 139 | |
| 140 | static void describe_detached_head(char *msg, struct commit *commit) |
| 141 | { |
| 142 | struct strbuf sb; |
| 143 | strbuf_init(&sb, 0); |
| 144 | parse_commit(commit); |
Jeff King | 2788631 | 2008-05-02 10:05:36 -0400 | [diff] [blame] | 145 | pretty_print_commit(CMIT_FMT_ONELINE, commit, &sb, 0, NULL, NULL, 0, 0); |
Daniel Barkalow | 782c2d6 | 2008-02-07 11:40:23 -0500 | [diff] [blame] | 146 | fprintf(stderr, "%s %s... %s\n", msg, |
| 147 | find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV), sb.buf); |
| 148 | strbuf_release(&sb); |
| 149 | } |
| 150 | |
| 151 | static int reset_to_new(struct tree *tree, int quiet) |
| 152 | { |
| 153 | struct unpack_trees_options opts; |
| 154 | struct tree_desc tree_desc; |
Linus Torvalds | bc052d7 | 2008-03-06 12:26:14 -0800 | [diff] [blame] | 155 | |
Daniel Barkalow | 782c2d6 | 2008-02-07 11:40:23 -0500 | [diff] [blame] | 156 | memset(&opts, 0, sizeof(opts)); |
| 157 | opts.head_idx = -1; |
| 158 | opts.update = 1; |
| 159 | opts.reset = 1; |
| 160 | opts.merge = 1; |
| 161 | opts.fn = oneway_merge; |
| 162 | opts.verbose_update = !quiet; |
Linus Torvalds | 34110cd | 2008-03-06 18:12:28 -0800 | [diff] [blame] | 163 | opts.src_index = &the_index; |
| 164 | opts.dst_index = &the_index; |
Daniel Barkalow | 782c2d6 | 2008-02-07 11:40:23 -0500 | [diff] [blame] | 165 | parse_tree(tree); |
| 166 | init_tree_desc(&tree_desc, tree->buffer, tree->size); |
| 167 | if (unpack_trees(1, &tree_desc, &opts)) |
| 168 | return 128; |
| 169 | return 0; |
| 170 | } |
| 171 | |
| 172 | static void reset_clean_to_new(struct tree *tree, int quiet) |
| 173 | { |
| 174 | struct unpack_trees_options opts; |
| 175 | struct tree_desc tree_desc; |
Linus Torvalds | bc052d7 | 2008-03-06 12:26:14 -0800 | [diff] [blame] | 176 | |
Daniel Barkalow | 782c2d6 | 2008-02-07 11:40:23 -0500 | [diff] [blame] | 177 | memset(&opts, 0, sizeof(opts)); |
| 178 | opts.head_idx = -1; |
| 179 | opts.skip_unmerged = 1; |
| 180 | opts.reset = 1; |
| 181 | opts.merge = 1; |
| 182 | opts.fn = oneway_merge; |
| 183 | opts.verbose_update = !quiet; |
Linus Torvalds | 34110cd | 2008-03-06 18:12:28 -0800 | [diff] [blame] | 184 | opts.src_index = &the_index; |
| 185 | opts.dst_index = &the_index; |
Daniel Barkalow | 782c2d6 | 2008-02-07 11:40:23 -0500 | [diff] [blame] | 186 | parse_tree(tree); |
| 187 | init_tree_desc(&tree_desc, tree->buffer, tree->size); |
| 188 | if (unpack_trees(1, &tree_desc, &opts)) |
| 189 | exit(128); |
| 190 | } |
| 191 | |
| 192 | struct checkout_opts { |
| 193 | int quiet; |
| 194 | int merge; |
| 195 | int force; |
| 196 | |
| 197 | char *new_branch; |
| 198 | int new_branch_log; |
Jay Soffian | 9ed36cf | 2008-02-19 11:24:37 -0500 | [diff] [blame] | 199 | enum branch_track track; |
Daniel Barkalow | 782c2d6 | 2008-02-07 11:40:23 -0500 | [diff] [blame] | 200 | }; |
| 201 | |
| 202 | struct branch_info { |
| 203 | const char *name; /* The short name used */ |
| 204 | const char *path; /* The full name of a real branch */ |
| 205 | struct commit *commit; /* The named commit */ |
| 206 | }; |
| 207 | |
| 208 | static void setup_branch_path(struct branch_info *branch) |
| 209 | { |
| 210 | struct strbuf buf; |
| 211 | strbuf_init(&buf, 0); |
| 212 | strbuf_addstr(&buf, "refs/heads/"); |
| 213 | strbuf_addstr(&buf, branch->name); |
| 214 | branch->path = strbuf_detach(&buf, NULL); |
| 215 | } |
| 216 | |
| 217 | static int merge_working_tree(struct checkout_opts *opts, |
Daniel Barkalow | 75ea38d | 2008-02-21 10:50:42 -0500 | [diff] [blame] | 218 | struct branch_info *old, struct branch_info *new) |
Daniel Barkalow | 782c2d6 | 2008-02-07 11:40:23 -0500 | [diff] [blame] | 219 | { |
| 220 | int ret; |
| 221 | struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file)); |
| 222 | int newfd = hold_locked_index(lock_file, 1); |
| 223 | read_cache(); |
| 224 | |
| 225 | if (opts->force) { |
| 226 | ret = reset_to_new(new->commit->tree, opts->quiet); |
| 227 | if (ret) |
| 228 | return ret; |
| 229 | } else { |
| 230 | struct tree_desc trees[2]; |
| 231 | struct tree *tree; |
| 232 | struct unpack_trees_options topts; |
Linus Torvalds | bc052d7 | 2008-03-06 12:26:14 -0800 | [diff] [blame] | 233 | |
Daniel Barkalow | 782c2d6 | 2008-02-07 11:40:23 -0500 | [diff] [blame] | 234 | memset(&topts, 0, sizeof(topts)); |
| 235 | topts.head_idx = -1; |
Linus Torvalds | 34110cd | 2008-03-06 18:12:28 -0800 | [diff] [blame] | 236 | topts.src_index = &the_index; |
| 237 | topts.dst_index = &the_index; |
Daniel Barkalow | 782c2d6 | 2008-02-07 11:40:23 -0500 | [diff] [blame] | 238 | |
| 239 | refresh_cache(REFRESH_QUIET); |
| 240 | |
| 241 | if (unmerged_cache()) { |
Junio C Hamano | 04c9e11 | 2008-02-23 15:45:19 -0800 | [diff] [blame] | 242 | error("you need to resolve your current index first"); |
| 243 | return 1; |
Daniel Barkalow | 782c2d6 | 2008-02-07 11:40:23 -0500 | [diff] [blame] | 244 | } |
Junio C Hamano | 04c9e11 | 2008-02-23 15:45:19 -0800 | [diff] [blame] | 245 | |
| 246 | /* 2-way merge to the new branch */ |
| 247 | topts.update = 1; |
| 248 | topts.merge = 1; |
| 249 | topts.gently = opts->merge; |
| 250 | topts.verbose_update = !opts->quiet; |
| 251 | topts.fn = twoway_merge; |
| 252 | topts.dir = xcalloc(1, sizeof(*topts.dir)); |
| 253 | topts.dir->show_ignored = 1; |
| 254 | topts.dir->exclude_per_dir = ".gitignore"; |
| 255 | tree = parse_tree_indirect(old->commit->object.sha1); |
| 256 | init_tree_desc(&trees[0], tree->buffer, tree->size); |
| 257 | tree = parse_tree_indirect(new->commit->object.sha1); |
| 258 | init_tree_desc(&trees[1], tree->buffer, tree->size); |
| 259 | |
| 260 | if (unpack_trees(2, trees, &topts)) { |
Daniel Barkalow | 782c2d6 | 2008-02-07 11:40:23 -0500 | [diff] [blame] | 261 | /* |
| 262 | * Unpack couldn't do a trivial merge; either |
| 263 | * give up or do a real merge, depending on |
| 264 | * whether the merge flag was used. |
| 265 | */ |
| 266 | struct tree *result; |
| 267 | struct tree *work; |
| 268 | if (!opts->merge) |
| 269 | return 1; |
| 270 | parse_commit(old->commit); |
| 271 | |
| 272 | /* Do more real merge */ |
| 273 | |
| 274 | /* |
| 275 | * We update the index fully, then write the |
| 276 | * tree from the index, then merge the new |
| 277 | * branch with the current tree, with the old |
| 278 | * branch as the base. Then we reset the index |
| 279 | * (but not the working tree) to the new |
| 280 | * branch, leaving the working tree as the |
| 281 | * merged version, but skipping unmerged |
| 282 | * entries in the index. |
| 283 | */ |
| 284 | |
| 285 | add_files_to_cache(0, NULL, NULL); |
| 286 | work = write_tree_from_memory(); |
| 287 | |
| 288 | ret = reset_to_new(new->commit->tree, opts->quiet); |
| 289 | if (ret) |
| 290 | return ret; |
| 291 | merge_trees(new->commit->tree, work, old->commit->tree, |
| 292 | new->name, "local", &result); |
| 293 | reset_clean_to_new(new->commit->tree, opts->quiet); |
| 294 | } |
| 295 | } |
| 296 | |
| 297 | if (write_cache(newfd, active_cache, active_nr) || |
| 298 | commit_locked_index(lock_file)) |
| 299 | die("unable to write new index file"); |
| 300 | |
| 301 | if (!opts->force) |
| 302 | show_local_changes(&new->commit->object); |
| 303 | |
| 304 | return 0; |
| 305 | } |
| 306 | |
Junio C Hamano | b56fca0 | 2008-02-20 19:42:53 -0800 | [diff] [blame] | 307 | static void report_tracking(struct branch_info *new, struct checkout_opts *opts) |
Junio C Hamano | 79a1e6b | 2008-02-16 17:17:09 -0800 | [diff] [blame] | 308 | { |
| 309 | /* |
| 310 | * We have switched to a new branch; is it building on |
| 311 | * top of another branch, and if so does that other branch |
| 312 | * have changes we do not have yet? |
| 313 | */ |
| 314 | char *base; |
| 315 | unsigned char sha1[20]; |
| 316 | struct commit *ours, *theirs; |
Junio C Hamano | 79a1e6b | 2008-02-16 17:17:09 -0800 | [diff] [blame] | 317 | char symmetric[84]; |
Junio C Hamano | b0030db | 2008-02-20 15:05:23 -0800 | [diff] [blame] | 318 | struct rev_info revs; |
| 319 | const char *rev_argv[10]; |
| 320 | int rev_argc; |
| 321 | int num_ours, num_theirs; |
| 322 | const char *remote_msg; |
Junio C Hamano | b56fca0 | 2008-02-20 19:42:53 -0800 | [diff] [blame] | 323 | struct branch *branch = branch_get(new->name); |
Junio C Hamano | 79a1e6b | 2008-02-16 17:17:09 -0800 | [diff] [blame] | 324 | |
Junio C Hamano | b0030db | 2008-02-20 15:05:23 -0800 | [diff] [blame] | 325 | /* |
| 326 | * Nothing to report unless we are marked to build on top of |
| 327 | * somebody else. |
| 328 | */ |
Junio C Hamano | b56fca0 | 2008-02-20 19:42:53 -0800 | [diff] [blame] | 329 | if (!branch || !branch->merge || !branch->merge[0] || !branch->merge[0]->dst) |
Junio C Hamano | 79a1e6b | 2008-02-16 17:17:09 -0800 | [diff] [blame] | 330 | return; |
| 331 | |
Junio C Hamano | 79a1e6b | 2008-02-16 17:17:09 -0800 | [diff] [blame] | 332 | /* |
Junio C Hamano | b0030db | 2008-02-20 15:05:23 -0800 | [diff] [blame] | 333 | * If what we used to build on no longer exists, there is |
| 334 | * nothing to report. |
Junio C Hamano | 79a1e6b | 2008-02-16 17:17:09 -0800 | [diff] [blame] | 335 | */ |
Junio C Hamano | b0030db | 2008-02-20 15:05:23 -0800 | [diff] [blame] | 336 | base = branch->merge[0]->dst; |
Junio C Hamano | 79a1e6b | 2008-02-16 17:17:09 -0800 | [diff] [blame] | 337 | if (!resolve_ref(base, sha1, 1, NULL)) |
| 338 | return; |
Junio C Hamano | b0030db | 2008-02-20 15:05:23 -0800 | [diff] [blame] | 339 | |
Junio C Hamano | 79a1e6b | 2008-02-16 17:17:09 -0800 | [diff] [blame] | 340 | theirs = lookup_commit(sha1); |
Junio C Hamano | b0030db | 2008-02-20 15:05:23 -0800 | [diff] [blame] | 341 | ours = new->commit; |
Junio C Hamano | 79a1e6b | 2008-02-16 17:17:09 -0800 | [diff] [blame] | 342 | if (!hashcmp(sha1, ours->object.sha1)) |
| 343 | return; /* we are the same */ |
| 344 | |
Junio C Hamano | b0030db | 2008-02-20 15:05:23 -0800 | [diff] [blame] | 345 | /* Run "rev-list --left-right ours...theirs" internally... */ |
| 346 | rev_argc = 0; |
| 347 | rev_argv[rev_argc++] = NULL; |
| 348 | rev_argv[rev_argc++] = "--left-right"; |
| 349 | rev_argv[rev_argc++] = symmetric; |
| 350 | rev_argv[rev_argc++] = "--"; |
| 351 | rev_argv[rev_argc] = NULL; |
Junio C Hamano | 79a1e6b | 2008-02-16 17:17:09 -0800 | [diff] [blame] | 352 | |
Junio C Hamano | b0030db | 2008-02-20 15:05:23 -0800 | [diff] [blame] | 353 | strcpy(symmetric, sha1_to_hex(ours->object.sha1)); |
| 354 | strcpy(symmetric + 40, "..."); |
| 355 | strcpy(symmetric + 43, sha1_to_hex(theirs->object.sha1)); |
| 356 | |
| 357 | init_revisions(&revs, NULL); |
| 358 | setup_revisions(rev_argc, rev_argv, &revs, NULL); |
| 359 | prepare_revision_walk(&revs); |
| 360 | |
| 361 | /* ... and count the commits on each side. */ |
| 362 | num_ours = 0; |
| 363 | num_theirs = 0; |
| 364 | while (1) { |
| 365 | struct commit *c = get_revision(&revs); |
| 366 | if (!c) |
| 367 | break; |
| 368 | if (c->object.flags & SYMMETRIC_LEFT) |
| 369 | num_ours++; |
| 370 | else |
| 371 | num_theirs++; |
| 372 | } |
| 373 | |
| 374 | if (!prefixcmp(base, "refs/remotes/")) { |
| 375 | remote_msg = " remote"; |
Junio C Hamano | 79a1e6b | 2008-02-16 17:17:09 -0800 | [diff] [blame] | 376 | base += strlen("refs/remotes/"); |
Junio C Hamano | b0030db | 2008-02-20 15:05:23 -0800 | [diff] [blame] | 377 | } else { |
| 378 | remote_msg = ""; |
Junio C Hamano | 79a1e6b | 2008-02-16 17:17:09 -0800 | [diff] [blame] | 379 | } |
Junio C Hamano | 79a1e6b | 2008-02-16 17:17:09 -0800 | [diff] [blame] | 380 | |
Junio C Hamano | b0030db | 2008-02-20 15:05:23 -0800 | [diff] [blame] | 381 | if (!num_theirs) |
| 382 | printf("Your branch is ahead of the tracked%s branch '%s' " |
| 383 | "by %d commit%s.\n", |
| 384 | remote_msg, base, |
| 385 | num_ours, (num_ours == 1) ? "" : "s"); |
| 386 | else if (!num_ours) |
Junio C Hamano | b56fca0 | 2008-02-20 19:42:53 -0800 | [diff] [blame] | 387 | printf("Your branch is behind the tracked%s branch '%s' " |
Junio C Hamano | b0030db | 2008-02-20 15:05:23 -0800 | [diff] [blame] | 388 | "by %d commit%s,\n" |
| 389 | "and can be fast-forwarded.\n", |
| 390 | remote_msg, base, |
| 391 | num_theirs, (num_theirs == 1) ? "" : "s"); |
| 392 | else |
| 393 | printf("Your branch and the tracked%s branch '%s' " |
| 394 | "have diverged,\nand respectively " |
| 395 | "have %d and %d different commit(s) each.\n", |
| 396 | remote_msg, base, |
| 397 | num_ours, num_theirs); |
| 398 | } |
Junio C Hamano | 79a1e6b | 2008-02-16 17:17:09 -0800 | [diff] [blame] | 399 | |
Daniel Barkalow | 782c2d6 | 2008-02-07 11:40:23 -0500 | [diff] [blame] | 400 | static void update_refs_for_switch(struct checkout_opts *opts, |
| 401 | struct branch_info *old, |
| 402 | struct branch_info *new) |
| 403 | { |
| 404 | struct strbuf msg; |
| 405 | const char *old_desc; |
| 406 | if (opts->new_branch) { |
| 407 | create_branch(old->name, opts->new_branch, new->name, 0, |
| 408 | opts->new_branch_log, opts->track); |
| 409 | new->name = opts->new_branch; |
| 410 | setup_branch_path(new); |
| 411 | } |
| 412 | |
| 413 | strbuf_init(&msg, 0); |
| 414 | old_desc = old->name; |
| 415 | if (!old_desc) |
| 416 | old_desc = sha1_to_hex(old->commit->object.sha1); |
| 417 | strbuf_addf(&msg, "checkout: moving from %s to %s", |
| 418 | old_desc, new->name); |
| 419 | |
| 420 | if (new->path) { |
| 421 | create_symref("HEAD", new->path, msg.buf); |
| 422 | if (!opts->quiet) { |
| 423 | if (old->path && !strcmp(new->path, old->path)) |
| 424 | fprintf(stderr, "Already on \"%s\"\n", |
| 425 | new->name); |
| 426 | else |
| 427 | fprintf(stderr, "Switched to%s branch \"%s\"\n", |
| 428 | opts->new_branch ? " a new" : "", |
| 429 | new->name); |
| 430 | } |
| 431 | } else if (strcmp(new->name, "HEAD")) { |
| 432 | update_ref(msg.buf, "HEAD", new->commit->object.sha1, NULL, |
| 433 | REF_NODEREF, DIE_ON_ERR); |
| 434 | if (!opts->quiet) { |
| 435 | if (old->path) |
| 436 | fprintf(stderr, "Note: moving to \"%s\" which isn't a local branch\nIf you want to create a new branch from this checkout, you may do so\n(now or later) by using -b with the checkout command again. Example:\n git checkout -b <new_branch_name>\n", new->name); |
| 437 | describe_detached_head("HEAD is now at", new->commit); |
| 438 | } |
| 439 | } |
| 440 | remove_branch_state(); |
| 441 | strbuf_release(&msg); |
Junio C Hamano | b0030db | 2008-02-20 15:05:23 -0800 | [diff] [blame] | 442 | if (!opts->quiet && (new->path || !strcmp(new->name, "HEAD"))) |
Junio C Hamano | b56fca0 | 2008-02-20 19:42:53 -0800 | [diff] [blame] | 443 | report_tracking(new, opts); |
Daniel Barkalow | 782c2d6 | 2008-02-07 11:40:23 -0500 | [diff] [blame] | 444 | } |
| 445 | |
Daniel Barkalow | 75ea38d | 2008-02-21 10:50:42 -0500 | [diff] [blame] | 446 | static int switch_branches(struct checkout_opts *opts, struct branch_info *new) |
Daniel Barkalow | 782c2d6 | 2008-02-07 11:40:23 -0500 | [diff] [blame] | 447 | { |
| 448 | int ret = 0; |
| 449 | struct branch_info old; |
| 450 | unsigned char rev[20]; |
| 451 | int flag; |
| 452 | memset(&old, 0, sizeof(old)); |
| 453 | old.path = resolve_ref("HEAD", rev, 0, &flag); |
| 454 | old.commit = lookup_commit_reference_gently(rev, 1); |
| 455 | if (!(flag & REF_ISSYMREF)) |
| 456 | old.path = NULL; |
| 457 | |
| 458 | if (old.path && !prefixcmp(old.path, "refs/heads/")) |
| 459 | old.name = old.path + strlen("refs/heads/"); |
| 460 | |
| 461 | if (!new->name) { |
| 462 | new->name = "HEAD"; |
| 463 | new->commit = old.commit; |
| 464 | if (!new->commit) |
| 465 | die("You are on a branch yet to be born"); |
| 466 | parse_commit(new->commit); |
| 467 | } |
| 468 | |
| 469 | /* |
| 470 | * If the new thing isn't a branch and isn't HEAD and we're |
| 471 | * not starting a new branch, and we want messages, and we |
| 472 | * weren't on a branch, and we're moving to a new commit, |
| 473 | * describe the old commit. |
| 474 | */ |
| 475 | if (!new->path && strcmp(new->name, "HEAD") && !opts->new_branch && |
| 476 | !opts->quiet && !old.path && new->commit != old.commit) |
| 477 | describe_detached_head("Previous HEAD position was", old.commit); |
| 478 | |
| 479 | if (!old.commit) { |
| 480 | if (!opts->quiet) { |
| 481 | fprintf(stderr, "warning: You appear to be on a branch yet to be born.\n"); |
| 482 | fprintf(stderr, "warning: Forcing checkout of %s.\n", new->name); |
| 483 | } |
| 484 | opts->force = 1; |
| 485 | } |
| 486 | |
Daniel Barkalow | 75ea38d | 2008-02-21 10:50:42 -0500 | [diff] [blame] | 487 | ret = merge_working_tree(opts, &old, new); |
Daniel Barkalow | 782c2d6 | 2008-02-07 11:40:23 -0500 | [diff] [blame] | 488 | if (ret) |
| 489 | return ret; |
| 490 | |
| 491 | update_refs_for_switch(opts, &old, new); |
| 492 | |
| 493 | return post_checkout_hook(old.commit, new->commit, 1); |
| 494 | } |
| 495 | |
Daniel Barkalow | 782c2d6 | 2008-02-07 11:40:23 -0500 | [diff] [blame] | 496 | int cmd_checkout(int argc, const char **argv, const char *prefix) |
| 497 | { |
| 498 | struct checkout_opts opts; |
| 499 | unsigned char rev[20]; |
| 500 | const char *arg; |
| 501 | struct branch_info new; |
| 502 | struct tree *source_tree = NULL; |
| 503 | struct option options[] = { |
| 504 | OPT__QUIET(&opts.quiet), |
| 505 | OPT_STRING('b', NULL, &opts.new_branch, "new branch", "branch"), |
| 506 | OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "log for new branch"), |
Miklos Vajna | 498a6e7 | 2008-04-24 01:04:48 +0200 | [diff] [blame] | 507 | OPT_SET_INT('t', "track", &opts.track, "track", |
Jay Soffian | 9ed36cf | 2008-02-19 11:24:37 -0500 | [diff] [blame] | 508 | BRANCH_TRACK_EXPLICIT), |
Daniel Barkalow | 782c2d6 | 2008-02-07 11:40:23 -0500 | [diff] [blame] | 509 | OPT_BOOLEAN('f', NULL, &opts.force, "force"), |
| 510 | OPT_BOOLEAN('m', NULL, &opts.merge, "merge"), |
Jay Soffian | b249b55 | 2008-02-18 05:20:20 -0500 | [diff] [blame] | 511 | OPT_END(), |
Daniel Barkalow | 782c2d6 | 2008-02-07 11:40:23 -0500 | [diff] [blame] | 512 | }; |
| 513 | |
| 514 | memset(&opts, 0, sizeof(opts)); |
| 515 | memset(&new, 0, sizeof(new)); |
| 516 | |
Denis Cheng | a3647be | 2008-03-02 18:05:05 +0800 | [diff] [blame] | 517 | git_config(git_default_config); |
Daniel Barkalow | 782c2d6 | 2008-02-07 11:40:23 -0500 | [diff] [blame] | 518 | |
Jay Soffian | 9ed36cf | 2008-02-19 11:24:37 -0500 | [diff] [blame] | 519 | opts.track = git_branch_track; |
Daniel Barkalow | 782c2d6 | 2008-02-07 11:40:23 -0500 | [diff] [blame] | 520 | |
| 521 | argc = parse_options(argc, argv, options, checkout_usage, 0); |
| 522 | if (argc) { |
| 523 | arg = argv[0]; |
| 524 | if (get_sha1(arg, rev)) |
| 525 | ; |
| 526 | else if ((new.commit = lookup_commit_reference_gently(rev, 1))) { |
| 527 | new.name = arg; |
| 528 | setup_branch_path(&new); |
| 529 | if (resolve_ref(new.path, rev, 1, NULL)) |
| 530 | new.commit = lookup_commit_reference(rev); |
| 531 | else |
| 532 | new.path = NULL; |
| 533 | parse_commit(new.commit); |
| 534 | source_tree = new.commit->tree; |
| 535 | argv++; |
| 536 | argc--; |
| 537 | } else if ((source_tree = parse_tree_indirect(rev))) { |
| 538 | argv++; |
| 539 | argc--; |
| 540 | } |
| 541 | } |
| 542 | |
| 543 | if (argc && !strcmp(argv[0], "--")) { |
| 544 | argv++; |
| 545 | argc--; |
| 546 | } |
| 547 | |
Jay Soffian | 9ed36cf | 2008-02-19 11:24:37 -0500 | [diff] [blame] | 548 | if (!opts.new_branch && (opts.track != git_branch_track)) |
Daniel Barkalow | 782c2d6 | 2008-02-07 11:40:23 -0500 | [diff] [blame] | 549 | die("git checkout: --track and --no-track require -b"); |
| 550 | |
| 551 | if (opts.force && opts.merge) |
| 552 | die("git checkout: -f and -m are incompatible"); |
| 553 | |
| 554 | if (argc) { |
| 555 | const char **pathspec = get_pathspec(prefix, argv); |
Alex Riesen | 301e42e | 2008-02-28 17:30:47 +0100 | [diff] [blame] | 556 | |
| 557 | if (!pathspec) |
| 558 | die("invalid path specification"); |
| 559 | |
Daniel Barkalow | 782c2d6 | 2008-02-07 11:40:23 -0500 | [diff] [blame] | 560 | /* Checkout paths */ |
| 561 | if (opts.new_branch || opts.force || opts.merge) { |
| 562 | if (argc == 1) { |
| 563 | die("git checkout: updating paths is incompatible with switching branches/forcing\nDid you intend to checkout '%s' which can not be resolved as commit?", argv[0]); |
| 564 | } else { |
| 565 | die("git checkout: updating paths is incompatible with switching branches/forcing"); |
| 566 | } |
| 567 | } |
| 568 | |
Daniel Barkalow | 7533687 | 2008-02-28 16:52:44 -0500 | [diff] [blame] | 569 | return checkout_paths(source_tree, pathspec); |
Daniel Barkalow | 782c2d6 | 2008-02-07 11:40:23 -0500 | [diff] [blame] | 570 | } |
| 571 | |
| 572 | if (new.name && !new.commit) { |
| 573 | die("Cannot switch branch to a non-commit."); |
| 574 | } |
| 575 | |
Daniel Barkalow | 75ea38d | 2008-02-21 10:50:42 -0500 | [diff] [blame] | 576 | return switch_branches(&opts, &new); |
Daniel Barkalow | 782c2d6 | 2008-02-07 11:40:23 -0500 | [diff] [blame] | 577 | } |