I found a way to update my computer and Remote/Origin branch to match when I rename a branch.
you can imagine renaming branch as:
git checkout oldBranchName
git branch -m renamedBranch
git push origin :oldBranchName renamedBranch // deletes old branch and push the renamed branch
My computer's local .git/refs/remotes/origin
is updated and any use of CLI or GUI(for git like Sourcetree) looks correct on my computer.
I am trying to find an easy/least risky way of updating the reference on a coworker's computer.
Something like:
# !!! Fake git command, only for clarifying a point
git update --refs
OR
git pull/fetch --refs
- Using
--set-upstream
to link branch "oldBranchName" to origin/renamedBranchName is confusing. - "git update-ref" seems dangerous and not really what I am looking for.
Note: I do understand that my coworker can delete the old branch and pull the new renamedBranch, however, this does not scale well to 10 coworkers.
CodePudding user response:
TL;DR
This is a bit long, so here's a TL;DR simple recipe for each of them:
run
git fetch --prune
;if they have a branch named
oldBranchName
, run:git branch -m oldBranchName renamedBranch git branch --set-upstream-to=origin/renamedBranch renamedBranch
(in that order). If they don't have that branch, these two commands will fail (harmlessly).
That's it: just the three commands (or one command if they have no oldBranchName
). The longer version below explains what this is for and gives an alternative sequence in the rare case that they have a really ancient Git version that lacks a few of these options.
Long
They need to run git fetch
to see the new branch name, which will show up in their clone as origin/renamedBranch
. They can run this command at any time (unlike git pull
, which can only be run at "safe" times). But there are two remaining issues:
- They will still have
origin/oldBranchName
, at least by default (see more below). - If they have a branch named
oldBranchName
, they still have that branch, still namedoldBranchName
. There is nothing wrong with this as far as Git is concerned: a branch name in your clone need not match the branch name in some other clone, including the clone over atorigin
. But it's confusing to work with.
To fix item 1, they need to tell their own Git software to "prune" remote-tracking names (the various origin/*
names) that no longer exist as branch names over on origin
. The key to understanding this is:
Each repository has its own branch names. Your branch names are yours and their branch names are theirs. We (humans) like to use the same name to keep things simple, but that's not a Git requirement.
Whenever they run
git fetch
—and remember thatgit pull
is a convenience command that runs two Git commands for you, with the first one beinggit fetch
; it's the second command that makesgit pull
hard—their Git software calls up some other Git software over atorigin
. The Git software over atorigin
lists out all the branch names in the clone atorigin
. So their Git (their software, operating in/on their repository) seesorigin
's Git's branch names. Their Git takes each of these names and renames it by stickingorigin/
in front of it.1 The result is a remote-tracking name (identified in part by theorigin/
in front, and in part because if you ask for colorized branch-name-listings, Git normally prints the remote-tracking names in red and the [local2] branch names in green).So, your Git's remote-tracking names—
origin/main
,origin/develop
, and so on—are your Git's way of remembering some other Git's names. That's it!
The normal update process, though, goes like this:
- you run
git fetch origin
(or justgit fetch
, which does the same thing); - your Git (your software in your repo) calls up their Git at
origin
; - they list out their branch names;
- your Git changes their branch names into your remote-tracking names, using the same renaming system every time;
- for each name obtained this way, your Git creates or updates the remote-tracking name.
If a branch name gets deleted on the remote, there's no corresponding remote-tracking name in step 4, so there's no update in step 5. If origin
had a branch xyzzy
yesterday and you ran git fetch
, your Git created origin/xyzzy
in your Git yesterday. Today, they have no xyzzy
, and your Git doesn't update your origin/xyzzy
. It doesn't remove it either. You're left with a stale remote-tracking name.
To clear out all the stale names, you have two Git commands:
git remote prune origin
, orgit fetch --prune origin
.
These supposedly do exactly the same thing. There was a range of Git versions (whose numbers I forget, back in the 1.7 or 1.8 days) where the code was a little broken and one of the two commands worked better than the other, so if you have a really old Git and one of the two commands isn't working right, try the other one. But both are supposed to:
- call up the Git over at
origin
and have it list out its branch names; - do the usual renaming to form remote-tracking names; and
- remove from your own repository any remote-tracking name for which there's no longer a branch name over on
origin
.
Step 3 here is the pruning part. Using git fetch --prune
will also do the regular git fetch
work, while git remote origin prune
will only do the pruning work.
Having now gotten rid of origin/renamedBranch
in your own local clone, you now have one remaining problem: your own branch named oldBranchName
still exists, if it existed before, and it still records origin/oldBranchName
as its upstream setting, if it did so before. So we must rename the branch. That's easy: git branch -m
does the trick. With one name:
git branch -m foo
Git renames the current branch so that its name is now foo
. That's not what we want, unless the current branch is oldBranchName
, but:
git branch -m oldBranchName renamedBranch
will rename the branch named oldBranchName
to renamedBranch
even if oldBranchName
is the current branch. So that handles the first part.
To fix the second part—that the branch we just renamed might still have origin/oldBranchName
set as its upstream—we just need to use git branch --set-upstream-to
.3 This takes, after the equals sign, the new upstream, and then an optional branch name like git branch -m
. If you don't give the optional branch name, it updates the current branch—not what we want unless oldBranchName
is the current branch—so again we provide the renamed branch name:
git branch --set-upstream-to=origin/renamedBranch renamedBranch
We've now:
- updated our
origin/*
names, clearing out the dead junk, viagit fetch --prune
; and - renamed our local branch and fixed its upstream setting
and that is everything we need to do.
1Internally, Git does this with a crazy-complicated system, but that's the normal end result.
2The word local here is redundant: your branch names are yours. They're all local. Even your remote-tracking names are local, they're just made up from the remote's branch names. They're made up freshly on every git fetch
!
3If you have a very old git branch
command, it may not have --set-upstream-to
. In this case it has --set-upstream
, which takes its arguments in confusing order: git branch --set-upstream renamedBranch origin/renamedBranch
.)