I am playing around with git.
Current status:
1) my local repo has one branch master_local.
2) the remote repo has one branch master_remote. (the remote's name is hehe_server)
3) my local .git/config
looks like
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[remote "hehe_server"]
url = /path/to/git/remote_main.git
fetch = +refs/heads/*:refs/remotes/hehe_server/*
[branch "master_local"]
remote = hehe_server
merge = refs/heads/master_remote
4) when I run git fetch
, git will fetch master_remote@hehe_server
to hehe_server/master_remote@local (aka, /.git/refs/remotes/hehe_server/master_remote@local)
5) when I run git branch -vv
, it says
* master_local 06022cf [hehe_server/master_remote] my_commit_msg
6) I understand that
i. master_local@local
is called "tracking branch"
ii. master_remote@hehe_server
is called "remote branch"
iii. hehe_server/master_remote@local
is called "remote-tracking branch"
7) My git version is git version 2.23.0.
8) I am using Mac 10.15.1
My question:
I want to rename hehe_server/master_remote@local
to hehe_server/master_haha@local
, while keeping everything else the same. Can I do that?
My experiment:
I tried playing with the fetch =
line inside .git/config
, but it is really confusing...
Test 1
change fetch = +refs/heads/*:refs/remotes/hehe_server/*
to fetch = +refs/heads/master_local:refs/remotes/hehe_server/master_remote
Result 1
git branch -vv says
* master_local 06022cf my_commit_msg
It seems master_local is no longer tracking master_remote.. I don't understand.
Test 2
change fetch = +refs/heads/*:refs/remotes/hehe_server/*
to fetch = +refs/heads/master_local:refs/remotes/hehe_server/master_haha
Result 2
same as Result 1
Git's terminology here is... not so great. In particular, the terms remote branch and tracking branch are not defined at all. See the gitglossary for what is defined.
People do, however, sometimes use the phrase remote branch to mean either remote-tracking branch name or the result of examining branch names on the remote . People—and the Git book—sometimes use the phrase tracking branch to mean a branch that has an upstream set .
Your definition of remote-tracking branch matches that in the Gitglossary. I dislike this term, as it leads to people dropping the adjective tracking and calling it a remote branch , which different people interpret differently. (I prefer to call this a remote-tracking name , although this isn't really much of an improvement. The main improvement, if there is one, lies not using the word "branch" again. :-) )
A more general term that does not suffer from all the above is ref (which is short for reference; I tend to spell it out the long way most of the time). If you look at the Git glossary, you'll see that this one is defined as:
A name that begins with
refs/
(egrefs/heads/master
) that points to an object name or another ref (the latter is called a symbolic ref). For convenience, a ref can sometimes be abbreviated when used as an argument to a Git command; see gitrevisions[7] for details. Refs are stored in the repository.
In any case: yes, you can produce arbitrary modifications of names as you're suggesting. The fetch =
line(s) in your .git/config
determine how each ref gets modified.
When you run git fetch
, the first step of the process is that your Git calls up some other Git. The URL by which your Git reaches that Git comes from:
git fetch https://github.com/owner/repo.git
for instance, orhehe_server
since you used git fetch hehe_server
.(There are several other ways to specify repository URLs as there was a lot of history that happened before remotes were invented. These are the two common methods.)
Having made this connection, the other Git then spills out all its refs. 1 You can observe this for yourself using git ls-remote
:
git ls-remote hehe_server
The output of this command is the set of refs, and hash IDs, that your Git sees when their Git presents them.
In any case, your Git can now take these refs and operate on them as instructed by the fetch =
settings. Each setting consists of a refspec . Refspecs are described in the git fetch
documentation , but mostly they consist of:
+
character meaning force ;:
; and The source name or pattern is matched against the names that the other Git presents. The resulting destination name or pattern is used to construct the name that your Git will create or update (or, with --prune
, delete if no input name matches it).
There are some odd constraints here. In particular, if you set up multiple source names that match to a single destination, or a single source that matches to multiple destinations, it doesn't work. For instance:
+refs/heads/master:refs/remotes/hehe_server/foo
+refs/heads/master:refs/remotes/hehe_server/bar
causes one source, master
, to map to two outputs, and:
[remote "hehe_server"]
fetch = +refs/heads/master:refs/remotes/hehe_server/foo
fetch = +refs/heads/develop:refs/remotes/hehe_server/foo
causes two sources, master
and develop
, to map to a single output. Neither can be handled usefully.
I want to rename
hehe_server/master_remote@local
tohehe_server/master_haha@local
, while keeping everything else the same. Can I do that?
Sort of yes, but mostly no. In particular, if you want to take their refs/heads/master
and call it refs/remotes/hehe_server/master_haha
, that part is easy:
fetch = +refs/heads/master:refs/remotes/hehe_server/master_haha
does the trick. But if you now want to take all the remaining names and handle them in the usual way:
fetch = +refs/heads/*:refs/remotes/origin/*
you have told Git that refs/heads/master
should become two names locally, because the second line maps the name to refs/remotes/origin/master
.
What this means is that to get this to work, you must:
remote.hehe_server.fetch
lines, one line per branch name, with the mapping you desire: everything except their master
maps as usual, and their master
maps weirdly.Every time the set of branch names on their server changes, you must repeat this process.
1 A new wire protocol allows server-side filtering of refs. Without this filtering, a repository with many tags and/or branches may spew multiple megabytes of unwanted data at your Git before your Git can get to having a useful conversation with it. But this—emitting all the refs—is what happens with the old protocol.
This is not an answer. I put my test script in this answer for others to get a whole picture.
#!/bin/bash
echo "
change branches' names
refs/heads/master_local
refs/remote/hehe_server/master_haha
master_remote@remote
"
PATHA=/path/to/an/empty/folder/
# Any subsequent(*) commands which fail will cause the shell script to exit immediately
set -e
# Makes the bash script to print out every command before it is executed except echo
trap '[[ $BASH_COMMAND != echo* ]] && echo "++++++RUN COMMAND: Line ${LINENO}: $BASH_COMMAND --- RESULT IS A BELOW"' DEBUG
cd $PATHA
echo "make a bare repo"
git --git-dir=$PATHA/remote_main.git init --bare
echo "clone"
cd $PATHA
git clone $PATHA/remote_main.git main --origin hehe_server
echo "------------------------------------------------------------------------------------------------------------------------------------"
echo "commit and push in master"
cd $PATHA/main
echo "test@master" > $PATHA/main/index.js
git add .
git commit -m "commit and push in master"
git push
echo "------------------------------------------------------------------------------------------------------------------------------------"
echo "commit and push in feature"
git checkout -b feature
echo "test@feature" > $PATHA/main/index.js
git add .
git commit -m "commit and push in feature"
git push -u hehe_server feature
git checkout master
echo "------------------------------------------------------------------------------------------------------------------------------------"
echo "commit and push in release"
git checkout -b release
echo "test@release" > $PATHA/main/index.js
git add .
git commit -m "commit and push in release"
git push -u hehe_server release
git checkout master
echo "------------------------------------------------------------------------------------------------------------------------------------"
echo "master@local --> master_local, master@hehe_server --> master_remote@hehe_server"
echo "# create and switch to the release branch"
git checkout -b master_local master
echo "# push the master_local branch to the remote and track it"
git push -u hehe_server master_local:master_remote
echo "# delete local master"
git branch -d master
echo "# delete remote master"
git --git-dir=$PATHA/remote_main.git symbolic-ref HEAD refs/heads/master_remote
git push --delete hehe_server master
echo "------------------------------------------------------------------------------------------------------------------------------------"
echo "hehe_server/master_remote@local --> hehe_server/master_haha@local"
echo "remove fetch= section first"
git config --unset remote.hehe_server.fetch
cat .git/config
echo "add first line for fetch="
git config --add remote.hehe_server.fetch "+refs/heads/master_remote:refs/remotes/hehe_server/master_haha"
echo "add second line for fetch="
git config --add remote.hehe_server.fetch "+refs/heads/*:refs/remotes/hehe_server/*"
cat .git/config
echo "check"
echo "see torek's warning: But if you now want to take all the remaining names and handle them in the usual way ...."
git branch -vv # outputs: * master_local 0870a94 [hehe_server/master_haha: gone] xxx
git fetch
git branch -vv # outputs: * master_local 0870a94 [hehe_server/master_haha] xxx
echo "why doesn't `git fetch --prune` remove the hehe_server/master_remote@local?"
ls -lah $PATHA/main/.git/refs/remotes/hehe_server
git fetch --prune
ls -lah $PATHA/main/.git/refs/remotes/hehe_server
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.