ChatGPT解决这个技术问题 Extra ChatGPT

Difference between git checkout --track origin/branch and git checkout -b branch origin/branch

Does anybody know the difference between these two commands to switch and track a remote branch?

git checkout -b branch origin/branch
git checkout --track origin/branch

I think both keep track of the remote branch so I can push my changes to the branch on origin, right?

Are there any practical differences?


V
VonC

The two commands have the same effect (thanks to Robert Siemer’s answer for pointing it out).

The practical difference comes when using a local branch named differently:

git checkout -b mybranch origin/abranch will create mybranch and track origin/abranch

git checkout --track origin/abranch will only create 'abranch', not a branch with a different name.

(That is, as commented by Sebastian Graf, if the local branch did not exist already.
If it did, you would need git checkout -B abranch origin/abranch)

Note: with Git 2.23 (Q3 2019), that would use the new command git switch:

git switch -c <branch> --track <remote>/<branch>

If the branch exists in multiple remotes and one of them is named by the checkout.defaultRemote configuration variable, we'll use that one for the purposes of disambiguation, even if the isn't unique across all remotes. Set it to e.g. checkout.defaultRemote=origin to always checkout remote branches from there if is ambiguous but exists on the 'origin' remote.

Here, '-c' is the new '-b'.

First, some background: Tracking means that a local branch has its upstream set to a remote branch:

# git config branch.<branch-name>.remote origin
# git config branch.<branch-name>.merge refs/heads/branch

git checkout -b branch origin/branch will:

create/reset branch to the point referenced by origin/branch.

create the branch branch (with git branch) and track the remote tracking branch origin/branch.

When a local branch is started off a remote-tracking branch, Git sets up the branch (specifically the branch..remote and branch..merge configuration entries) so that git pull will appropriately merge from the remote-tracking branch. This behavior may be changed via the global branch.autosetupmerge configuration flag. That setting can be overridden by using the --track and --no-track options, and changed later using git branch --set-upstream-to.

And git checkout --track origin/branch will do the same as git branch --set-upstream-to):

 # or, since 1.7.0
 git branch --set-upstream upstream/branch branch
 # or, since 1.8.0 (October 2012)
 git branch --set-upstream-to upstream/branch branch
 # the short version remains the same:
 git branch -u upstream/branch branch

It would also set the upstream for 'branch'.

(Note: git1.8.0 will deprecate git branch --set-upstream and replace it with git branch -u|--set-upstream-to: see git1.8.0-rc1 announce)

Having an upstream branch registered for a local branch will:

tell git to show the relationship between the two branches in git status and git branch -v.

directs git pull without arguments to pull from the upstream when the new branch is checked out.

See "How do you make an existing git branch track a remote branch?" for more.


@VonC I been looking for that little detail you happened to mention as extra information. In my case, I was curious why I had some my branches allow me to git pull, whereas some branches would ask for a remote branch to pull from. It turns out that if you, in your first-time, are checking out a remote branch that your peer created, git goes on and adds branch.<BNAME>.remote=origin to the local gitconfig. Which then allows you to issue git pull. However, if you are the one creating the branch git checkout -b BNAME, then git -of course- does not know. So you should specify its remote.
@batilc "It turns out that if you, in your first-time, are checking out a remote branch that your peer created,"; yes, reading git-scm.com/docs/git-checkout, I see: "If <branch> is not found but there does exist a tracking branch in exactly one remote (call it <remote>) with a matching name, treat as equivalent to $ git checkout -b <branch> --track <remote>/<branch>"
@VonC I found a better configuration for this. setting up branch.autoSetupMerge to always simply performs what we are talking about. This setting defaults to true, which means the tracking will be performed only when checking out a remote branch. true does not setup tracking for locally created branches.
@batilc I agree. I tend to not use always, as I prefer to explicitly set tracking, but in your case, that should be the right setting.
"git branch --set-upstream-to branch upstream/branch" is not the correct syntax. it should be: "git branch --set-upstream-to upstream/branch branch"
R
Robert Siemer

There is no difference at all!

1) git checkout -b branch origin/branch

If there is no --track and no --no-track, --track is assumed as default. The default can be changed with the setting branch.autosetupmerge.

In effect, 1) behaves like git checkout -b branch --track origin/branch.

2) git checkout --track origin/branch

“As a convenience”, --track without -b implies -b and the argument to -b is guessed to be “branch”. The guessing is driven by the configuration variable remote.origin.fetch.

In effect, 2) behaves like git checkout -b branch --track origin/branch.

As you can see: no difference.

But it gets even better:

3) git checkout branch

is also equivalent to git checkout -b branch --track origin/branch if “branch” does not exist yet but “origin/branch” does1.

All three commands set the “upstream” of “branch” to be “origin/branch” (or they fail).

Upstream is used as reference point of argument-less git status, git push, git merge and thus git pull (if configured like that (which is the default or almost the default)).

E.g. git status tells you how far behind or ahead you are of upstream, if one is configured.

git push is configured to push the current branch upstream by default2 since git 2.0.

1 ...and if “origin” is the only remote having “branch” 2 the default (named “simple”) also enforces for both branch names to be equal


P
Pat

The book seems to indicate that those commands yield the same effect:

The simple case is the example you just saw, running git checkout -b [branch] [remotename]/[branch]. If you have Git version 1.6.2 or later, you can also use the --track shorthand:

$ git checkout --track origin/serverfix 
Branch serverfix set up to track remote branch serverfix from origin. 
Switched to a new branch 'serverfix' 

To set up a local branch with a different name than the remote branch, you can easily use the first version with a different local branch name:

$ git checkout -b sf origin/serverfix

That's particularly handy when your bash or oh-my-zsh git completions are able to pull the origin/serverfix name for you - just append --track (or -t) and you are on your way.


G
Green

You can't create a new branch with this command

git checkout --track origin/branch

if you have changes that are not staged.

Here is example:

$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   src/App.js

no changes added to commit (use "git add" and/or "git commit -a")

// TRY TO CREATE:

$ git checkout --track origin/new-branch
fatal: 'origin/new-branch' is not a commit and a branch 'new-branch' cannot be created from it

However you can easily create a new branch with un-staged changes with git checkout -b command:

$ git checkout -b new-branch
Switched to a new branch 'new-branch'
M       src/App.js

keep in mind that both of the commands in the questions are for tracking an existing remote branch (origin/branch)
@Green The test you do is with origin/new-branch instead of origin/branch. Are you aware of that?