admin管理员组

文章数量:1312658

Scenario 1

Suppose I have the following stanza in .git/config:

[remote "origin"]
    fetch = +refs/heads/*:refs/remotes/origin/*

With this configuration:

  1. Running git fetch origin will fetch all branches.
  2. Running git fetch origin my_branch will fetch origin's copy of my_branch and update the tracking branch at refs/remotes/origin/my_branch.

Scenario 2

Now suppose I have the following stanza instead, which names two explicit branches that are not my_branch.

[remote "origin"]
    fetch = +refs/heads/master:refs/remotes/origin/master
    fetch = +refs/heads/all-green:refs/remotes/origin/all-green

With this configuration:

  1. Running git fetch origin will only fetch master and all-green.
  2. Running git fetch origin my_branch will fetch the branch, but not update any tracking branch. If I want to update the tracking branch, I must explicitly say git fetch origin my_branch:refs/remotes/origin/my_branch.

Question

Is it possible to configure git in such a way that the following two statements are true?

  1. git fetch origin my_branch will update local tracking branches according to the refspec in Scenario 1 for all possible values of my_branch.
  2. git fetch origin fetches only those branches that have previously been explicitly fetched (e.g. they already have a remote-tracking branch) rather than all branches that match the refspec.

In other words, I would like to use the refspec to determine the branch mapping automatically when I give a remote branch name to fetch, but not to determine the set of branches that is fetched when I merely say git fetch origin without a branch name.

My current approach is to just edit the git config (manually or using a script) for every remote branch I want to track, but I ask this question to see if there's a more direct way to get the behavior I want.

Scenario 1

Suppose I have the following stanza in .git/config:

[remote "origin"]
    fetch = +refs/heads/*:refs/remotes/origin/*

With this configuration:

  1. Running git fetch origin will fetch all branches.
  2. Running git fetch origin my_branch will fetch origin's copy of my_branch and update the tracking branch at refs/remotes/origin/my_branch.

Scenario 2

Now suppose I have the following stanza instead, which names two explicit branches that are not my_branch.

[remote "origin"]
    fetch = +refs/heads/master:refs/remotes/origin/master
    fetch = +refs/heads/all-green:refs/remotes/origin/all-green

With this configuration:

  1. Running git fetch origin will only fetch master and all-green.
  2. Running git fetch origin my_branch will fetch the branch, but not update any tracking branch. If I want to update the tracking branch, I must explicitly say git fetch origin my_branch:refs/remotes/origin/my_branch.

Question

Is it possible to configure git in such a way that the following two statements are true?

  1. git fetch origin my_branch will update local tracking branches according to the refspec in Scenario 1 for all possible values of my_branch.
  2. git fetch origin fetches only those branches that have previously been explicitly fetched (e.g. they already have a remote-tracking branch) rather than all branches that match the refspec.

In other words, I would like to use the refspec to determine the branch mapping automatically when I give a remote branch name to fetch, but not to determine the set of branches that is fetched when I merely say git fetch origin without a branch name.

My current approach is to just edit the git config (manually or using a script) for every remote branch I want to track, but I ask this question to see if there's a more direct way to get the behavior I want.

Share Improve this question asked Feb 1 at 5:01 merlin2011merlin2011 75.6k47 gold badges212 silver badges356 bronze badges 5
  • What do you mean by "for all possible values of my_branch"? A branch only has one value. – Tim Roberts Commented Feb 1 at 5:08
  • @TimRoberts, I mean that I want the command to work for any branch that I specify, without having to name each one manually in the config. For example, if I replace my_branch with the string TimRoberts/branch1 or the string merlin2011/branch2, it should still work. – merlin2011 Commented Feb 1 at 5:10
  • about your requirements: do you want git myfetch origin branchfoo to: a. just update origin/branchfoo ref ? b. update origin/branchfoo and also create a local tracking branch ? or c. add branchfoo in the list of default branches in the .git/config file for that remote ? – LeGEC Commented Feb 1 at 9:03
  • the 2 generic ways to add aliases or scripts: 1. if an alias starts with ! ..., ... can be any shell command, 2. if you create an executable script named git-foo, make it executable and somehow accessible from the PATH, then you can type git foo ... as if it were an alias or a new subcommand – LeGEC Commented Feb 1 at 9:05
  • @LeGEC Good question! I want it to do a) and b) and not c). I am interpreting c) to mean actually creating a local branch that shows up in the output of git branch, but if c just means the branch gets updated by default when I do git fetch origin, then I also mean c. – merlin2011 Commented Feb 1 at 9:12
Add a comment  | 

2 Answers 2

Reset to default 1

edit: this works fine but OP found the --refmap option I'd never noticed, that makes a much better answer, this is still useful as context/samplecode so:

It's very easy to write a ~update the factory-default tracking branches even for branches I'm not usually tracking~ convenience, the payload is just

awk '$2=="branch" { print "git update-ref refs/remotes/origin/"$3,$1 }' \
        $(git rev-parse --git-dir)/FETCH_HEAD | sh -x

and you can invoke that however you want. It might be your best option, because I think this in the fetch refspec docs

Unlike when pushing with git-push[1], there is no configuration which’ll amend these rules, and nothing like a pre-fetch hook analogous to the pre-receive hook.

means "no." Which does make sense, fetching is often scripted for custom workflows already, some of them so common support has been provided in a factory-supplied convenience: git pull does any of several really common fetch-plus-custom-followup sequences.

For another option, you could also customize an alternate remote,

git config remote.onebranch.url $(git config remote.origin.url)
git config remote.onebranch.fetch +refs/heads/*:refs/remotes/origin/*

after which git fetch onebranch my_branch will update the origin tracking branch if needed.

Given that the answer is "No", I am posting additional workarounds:

Workaround 1: Add an alias for the specific remote.

git config --global alias.fetchbranch "fetch --refmap=+refs/heads/*:refs/remotes/origin/*"

Workaround 1a: Add an alias for any remote

Add the following manually to the ~/.gitconfig:

[alias]
  ffetch = "!f() { git fetch --refmap=\"+refs/heads/*:refs/remotes/${1}/*\" \"$@\"; }; f"

Note that both of the above workarounds, as well as the accepted answer, do not cause git fetch origin to correctly fetch the tracked branch.

Workaround 2: Script for adding to git config

The only method that actually causes git fetch without specifying a branch to automatically pick up updates appears to be adding the refspec into the git config:

git-track

remote_branch_name="$1"
remote="origin"
if [[ $# -gt 1 ]]; then
  remote="$2"
fi

refspec="refs/heads/${remote_branch_name}:refs/remotes/origin/${remote_branch_name}"

# Check if this config already exists.
if git config --get "remote.${remote}.fetch" "$refspec"; then
  >&2 echo "Branch ${remote_branch_name} from ${remote} is already tracked."
  exit 0
fi

# Check if the remote has the branch.
if ! git fetch "$remote" "refs/heads/${remote_branch_name}" 2>/dev/null; then
  >&2 echo "Branch ${remote_branch_name} does not exist on ${remote}."
  exit 1
fi

# Actually add the refspec
git config --add "remote.${remote}.fetch" "+${refspec}"

# Do the initial fetch as a convenience.
git fetch "${remote}" "${remote_branch_name}"

I paired this with a second script to untrack:

git-untrack

remote_branch_name="$1"
remote="origin"
if [[ $# -gt 1 ]]; then
  remote="$2"
fi

refspec="refs/heads/${remote_branch_name}:refs/remotes/origin/${remote_branch_name}"

# For no-op case.
if ! git config --get "remote.${remote}.fetch" "$refspec"; then
  >&2 echo "Branch ${remote_branch_name} from ${remote} is already untracked."
  exit 0
fi

git config --unset-all remote.origin.fetch "\+${refspec}"

本文标签: