How to do Git pull only on a branch in shell script?

放肆的年华 提交于 2019-12-08 05:11:25

问题


I have Bourne shell script that will build the HEAD of a specified remote branch or a specified remote tag. The branch/tag is specified as input argument to the script.

In the script I perform a git fetch to update all local info with the remote info. Then I perform a git checkout branch/tag.

I then like to perform a conditional git pull (if I understand it correctly). In case it concerns a branch I like to perform a git pull to merge from the remote branch with the same name. However, in case of a specified tag, I don't want to perform a git pull (it results in an error).

How can I best solve this conditional git pull? Or more basic: which git commands do I need in a shell script to build a remote branch or remote tag?

  • Ed

回答1:


I think at this point (based on discussion above) that you're getting a bit mixed up with what it means to be "on a branch", etc.

Deep inside git, when you do a git checkout NAME, git resolves NAME to a particular, specific commit. That commit is attached to a "tree"—a directory tree full of files—and checkout attempts to get that specific tree into your work-tree (while not clobbering any unsaved files, etc., but let's assume that your work tree is pristine in all cases, to simplify things here).

The other thing checkout does is, if the name is one of your own branch-names, it records your "current branch" as the HEAD. However, it's possible for that name not to name "your" branch. This is true if you check out an explicit "remote" branch:

$ git checkout remotes/origin/master
Note: checking out 'remotes/origin/master'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b new_branch_name

HEAD is now at fb4a1b2... (some commit message)

This is also what happens if you check out an explicit tag:

$ git checkout v1.0
Previous HEAD position was fb4a1b2... (some commit message)
HEAD is now at cfb9904... (another commit message)

In all of these cases, you have a perfectly good work tree that is what is represented by those particular commit IDs. You are just no longer "on a branch" at all ('detached HEAD' state). (You can "get back on a branch" by checking out some local branch, or by creating a new local branch with git checkout -b newname whose tip is then what HEAD is now at ...)

Now, say you have some "remote branch" that you are intentionally tracking:

$ git checkout GRONKLE # my local branch, which tracks remotes/origin/GRONKLE
$ git pull

The "pull" here consists of a fetch plus a merge. The "fetch" step uses the appropriate underlying transport to bring over any new commits on the remote branch. That gets you "what they did". The "merge" step then tries to apply "what they did" to "what you have" in your local branch.

Let's say, for example, that "what you have" was completely in sync with "what they did" yesterday. Then, today, you changed dir1/file1 and dir1/file2 (and committed that). Meanwhile, "they" changed dir2/file3 (and committed that into your remotes/origin repo). When you do a "git fetch", that gets their updates to dir2/file3 and makes remotes/origin/GRONKLE name "what they have". Your branch is now behind ... by 1 commit.

When you continue on to do a "git merge", however, that applies their changes to dir2/file3, but also keeps your changes to dir1/file1 and dir1/file2. This gives you a new commit, which is the "merge commit". Your branch is now ahead by one commit.

If you want to build "what they have"—which is what you said in the original question—then you don't want your changes to dir1/file1 and dir1/file2 sitting around. You can simply check out remotes/origin/GRONKLE (in this example) and you will have precisely what they gave you, no more and no less.

If you want to build "what they have, plus what I have", then you need to merge and/or rebase, as @Adam Dymitruk said. But that's not "what they have". And—in response to your response to him—you're never "on a tag" at all. A tag is just the name of a specific commit, and if you check out that tag, you're in that same "detached HEAD" state yet again. If you want to make changes to "a new branch that begins where that tag begins" (see footnote), you need to create said new branch, starting from that tag.

If you want to create a new (local) branch starting from some given commit, there are two easy ways:

$ git checkout <commit>

(you're now on that commit and can look around and see if that's really the one you want) followed by:

$ git checkout -b new_local_branch

Or, if you're sufficiently sure of your starting point:

$ git checkout -b new_local_branch <commit>

(and about a half dozen more ways, using git branch, but the above two suffice).


Footnote: branches don't really "begin" anywhere, after all is said and done. More precisely, you get yourself a starting point—by checking out some particular commit-ID—and do any work you like from there forward, making new commits, etc., but once you put a branch label to remember that work, the branch label simply follows the "tip" of that work. Until then, if you're in "detached HEAD" state, the primary reference to any new commits you make is your detached HEAD. It's easy to "lose hold" of that reference, though, if you don't make a new branch, although if you poke around inside git you can find all the extra places it saves things for you, typically for up to three months.


回答2:


You want to fetch. Then merge, rebase or reset.




回答3:


Note: if your remote repo has a branch and a tag with the same name, your fetch might not fetch the branch after Git 2.19 (Q3 2018)

See commit 60650a4 (01 Aug 2018) by Junio C Hamano (gitster).
Helped-by: Jonathan Tan (jhowtan), and Jonathan Nieder (artagnon).
(Merged by Junio C Hamano -- gitster -- in commit 72c11b7, 17 Aug 2018)

remote: make refspec follow the same disambiguation rule as local refs

When matching a non-wildcard LHS (see here) of a refspec against a list of refs, find_ref_by_name_abbrev() returns the first ref that matches using any DWIM rules used by refname_match() in refs.c, even if a better match occurs later in the list of refs.

This causes unexpected behavior when (for example) fetching using the refspec "refs/heads/s:<something>" from a remote with both "refs/heads/refs/heads/s" and "refs/heads/s"; even if the former was inadvertently created, one would still expect the latter to be fetched.

Similarly, when both a tag T and a branch T exist, fetching T should favor the tag, just like how local refname disambiguation rule works.
But because the code walks over ls-remote output from the remote, which happens to be sorted in alphabetical order and has refs/heads/T before refs/tags/T, a request to fetch T is (mis)interpreted as fetching refs/heads/T.

Update refname_match(), all of whose current callers care only if it returns non-zero (i.e. matches) to see if an abbreviated name can mean the full name being tested, so that it returns a positive integer whose magnitude can be used to tell the precedence, and fix the find_ref_by_name_abbrev() function not to stop at the first match but find the match with the highest precedence.



来源:https://stackoverflow.com/questions/9729801/how-to-do-git-pull-only-on-a-branch-in-shell-script

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!