简体   繁体   中英

can “remote branch”, “remote-tracking branch” and “tracking branch” have different names?

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/ (eg refs/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:

  • the command line, if you run git fetch https://github.com/owner/repo.git for instance, or
  • the stored URL under the name of a remote , in this case the one stored under hehe_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:

  • an optional leading + character meaning force ;
  • a source name or pattern;
  • a colon character : ; and
  • a destination name or pattern.

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 to hehe_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:

  • contact the other Git
  • get a complete list of all of their branch names
  • write a new set of 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM