how to reset a git working tree to an updated commitish - bash

I need to extend a given tool with a Bash script that is supposed to work with Linux and MacOS. The script gets 2 parameters:
A repository location (file system, ssh, http(s), ...)
A commitish, e.g. branch, tag, commit hash
I have no influence on the parameters
The result of the script's run should be that
the repository is cloned to a fixed destination (always the same for one repository)
the repositorie's working tree should correspond to the latest state of the comittish (e.g. if it was a branch the tip of that branch)
If the repository does not (yet) exist locally, the procedure is as simple as
git clone $REPO_SOURCE $REPO_DIR
cd $REPO_DIR
git checkout $REPO_REF
My question: Consider a repository is already cloned to /repos/foo. After an obvios git fetch, how to I update that repository to the provided $REPO_REF?
If $REPO_REF was a branch, a git checkout $REPO_REF && git pull should work
If it was a commit hash, there was no update needed (just git checkout $REPO_REF?)
If it was a tag, then the tag might have been moved on the origin, how to handle this?
How to handle other edge cases?
Is there a simple reset-repository-to-this-commitsh way, so the repository behaves just as if it was freshly cloned?
Side nodes:
The same repository might be used with different commitish's, but only sequentially: It is guaranteed that the script isn't invoked more than once at the same time
All external changes to the repository might be always discarded without notification
While deleting and cloning the repository would work, it is impractical due to their sizes and it being an ugly solution
No (git) changes are needed, so checking out a detached head is okay

The only totally-foolproof yet convenient way is to have the other Git (the one you might be cloning, but might not) resolve the name for you. Then you have a hash ID and a hash ID is universal.
If the name is a branch or tag name, you can use git ls-remote to achieve that step. If it might be some other formulation (e.g., master~13) you're out of luck here. So, if you need to resolve the name locally:
If tag discipline is obeyed, no tag will ever move. This means that if you have an existing clone that has the tag, it has the right tag, and you're OK here, and if you have an existing clone that doesn't have the tag, you can add the tag and resolve it.
If tag discipline is not obeyed, you'd have to delete and re-create the tags (yuck), or else re-invent remote tags: copy their refs/tags/* names to your refs/rtags/<remote>/* name-space. See Git - Checkout a remote tag when two remotes have the same tag name.
If you have a branch name or something relative to a branch name, turn the branch name into your own remote-tracking name (e.g., replace master~13 with refs/remotes/origin/master~13) and resolve it.
In any case, you now have a hash ID and can use detached HEAD mode.

Using a "standard" git clone you could to this:
# cleanup old cruft
git reset --hard HEAD
git clean -fdx
# detach from current branch (if on any)
git checkout --detach
# delete all local branches
git for-each-ref --format="%(refname:strip=2)" refs/heads |xargs -r git branch -D
# fetch and update all remote refs and tags
git fetch --force --all --tags --prune --prune-tags
# checkout
git checkout "$COMMITISH"
That way you can rely on git checkout to do its job as usual and you don't need to replicate any of its heuristics, shortcuts etc.

Related

Git showing 'up-to-date' when pulling branch from bash file

I have a bash script that pulls and builds some source code. If I am on the master branch, it works as expected, however if I change to a different branch it says it is already up to date even if there are pushed changes. If I do a git pull outside the script from the cmd line, it pulls as expected.
deploy.sh
echo | git branch
echo "git pull ..."
git pull https://tech-dev:password#bitbucket.org/mycompany/pow-wow.git
Output
./deploy.sh
master
* spring3upgrade
git pull ...
From https://bitbucket.org/mycompany/pow-wow
* branch HEAD -> FETCH_HEAD
Already up-to-date.
Question
How do I get it to pull from the branch I am currently on in the bash script?
why it doesn't work
If you are pulling using an explicit URL (as displayed in your question) :
there is no default refspec, so only the remote HEAD (the default branch) is fetched
there is no default "remote branch" configured for your checked out branch, so git pull will merge in whatever that default branch points to (ie: it will try to merge origin/master, not origin/spring3upgrade)
how to fix it
The simplest way is to define a named remote (e.g: origin), let git set up its default configuration, and have named remote tracking branches:
git remote add origin <URL>
git fetch
# for branches that already exist locally:
git switch <branch>
git branch -u origin/branch
# for remote branches not checked out locally:
git switch <branch> # will automatically take 'origin/<branch>' as a base,
# and set it as the upstream branch
If you have a special need which requires to not name the remote: you may provide the refspec you need on the command line, probably:
# pull the branch which has the same name as your local branch:
git pull <repo> "$(git branch --show-current)"
You need to provide specific credentials to access your remote. There are many ways to do that :
a pretty common way is to go through ssh: create an ssh key, configure your central server to accept the public key for a CI (you choose the name ...) dedicated user, and set up your builder agent to access your repo through ssh with that key
or using https, set up a credentials manager (see the link your posted in your comment, or git help credentials), or the many http.* settings in git help config

GIT : How to get latest changes from master branch to the custom branch using git commands?

Branch "abcd/child" is created from "abcd/master" . Changes are made to "abcd/child" and meanwhile "abcd/master" also have added changes. Now how to make sure the latest changes are pulled from "abcd/master" is available in "abcd/child" using git commands in git bash?
Assuming abcd is the name of your remote, here's how I'd do it:
git checkout child
git pull
git merge abcd/master
git push
When you checkout child, it'll probably say "set up to track remote abcd" or similar.
The git pull command does two things: It fetches all updates from the server (on all branches) to your local git repo, and it updates your branch to match what's on the remote.
The git merge abcd\master means to specifically bring in all of the commits that are on the remote's copy of the master branch. That's important because you may not have updated your local master to have all those commits.
Note also that you might get conflicts in the git merge if both abcd and master have edited the same sections of the same file. There's lots of help in resolving git merge conflicts.
Also: You want to make sure everything works after the merge. It's possible that the changes on master broke an API that you were using, so you may need to make edits to deal with that.
Update: My assumption about abcd being the name of the remote is wrong.
First, get the name of your remote with the git remote command. Mine goes like this.
git remote
origin
So I only have one remote, and I call it origin. That's pretty common. If you've got more than one remote, it'll be trickier.
So, with "origin" as the remote it goes like this:
git checkout abcd/child
git pull
git merge origin/abcd/master
git push
Obviously substitute the name of your remote if it's not origin.
Same caveats apply about conflicts and making sure it works.

How to get a git repository's default branch in Ruby's "rugged" gem?

Related to git - how to get default branch?
I'm trying to port a Bash script to Ruby. The script uses git symbolic-ref --short HEAD to get a repo's default branch.
Is there a way to combine rugged's repo.branches.each_name(:local) with some other command to find the default branch?
Or am I thinking too complicated and there is a useful default in rugged that I am missing? The script needs to continue by creating a new branch off the default/latest one, check-it out, commit something standardized, push the new branch, and open a pull request against that base branch.

Git missing branch name on remote

I have following situation, github is my origin, and I see there is branch with name Release1.0. I see it also in my console:
xyz#yxz MINGW64 /c/myreponame (Development)
$ git remote show origin
* remote origin
Fetch URL: https://github.com/myorganization/myreponame
Push URL: https://github.com/myorganization/myreponame
HEAD branch: Development
Remote branches:
Release1.0 tracked
Development tracked
But when I try to switch to that branch I see:
xyz#yxz MINGW64 /c/myreponame (Development)
$ git checkout --track Release1.0 origin/Release1.0
fatal: Missing branch name; try -b
I think important note, here Im using Windows, may it is some problem with upper case in branch name? Windows is case insensitive.
UPDATED to reflect an edge case that would mess with the checkout shortcut
This means that you haven't yet created the local branch Release1.0. The branch exists on the remote, and you have a tracking ref for it - that's what the show origin output is telling you.
But the checkout syntax you're using isn't correct for setting up the local branch to track the remote branch. Simply
git checkout Release1.0
will likely work. This is a special case "shortcut" built into the checkout command, because it's common to want to "copy" a branch from the one available remote. The cases where it won't work:
If there are multiple remotes with the same branch name, then git will not know which one to check out so will refuse to use the shortcut.
If you have a tag with the same name, git will check that out (in detached HEAD state) instead. In that case you can still create the branch (without the shortcut; see below), but beware of having a tag and a branch with the same name. (In practice git has fairly sensible rules for how any given command will behave, and you always can disambiguate between the tag and the branch; but it's better not to have to think about such things.)
The other way, besides the shortcut, to create the branch: you can use the -b option (as the error message indicates), as that's the more general way to create a branch during a checkout operation. Or you can use git branch.
Unless there's more going on than you've spelled out, this is not a windows / case sensitivity issue. Note that saying "Windows is case insensitive" is not meaningful. It is correct that Windows typically uses file system with case-insensitive filenames, and this would cause problems if you tried to create two branches whose names are identical except for case. But nothing says that branch names have to be all-lowercase (which would actually be a peculiar form of case-sensitivity).

Automatically change branch in git

I noticed that, on Windows, when I create a new branch in my repository using the shell, Git doesn't change the branch I am on. When using Linux, it does. How's that? Is there a possibility to change that, to the way Git in Linux does? (I'm very new to Git.)
To create a new branch and switch to it at the same time, use git checkout -b <branchname>.
I noticed that, on Windows, when I create a new branch in my repository using the shell, Git doesn't change the branch I am on
The commands for branching are the following:
# create branch and stay on the current branch
git branch <branch name>
# create a branch and switch to the new branch
git checkout -b <branch name>
# You can always create branch from any given commit/tag/branch etc you need
git checkout -b <SHA-1>/tag/branch
You can find in this answer a very useful information what it does and how to do it:
How to move HEAD back to a previous location? (Detached head)

Resources