Related
Using gitk log, I could not spot a difference between the effect of git merge and git merge --no-ff. How can I observe the difference (with a git command or some tool)?
The --no-ff flag prevents git merge from executing a "fast-forward" if it detects that your current HEAD is an ancestor of the commit you're trying to merge. A fast-forward is when, instead of constructing a merge commit, git just moves your branch pointer to point at the incoming commit. This commonly occurs when doing a git pull without any local changes.
However, occasionally you want to prevent this behavior from happening, typically because you want to maintain a specific branch topology (e.g. you're merging in a topic branch and you want to ensure it looks that way when reading history). In order to do that, you can pass the --no-ff flag and git merge will always construct a merge instead of fast-forwarding.
Similarly, if you want to execute a git pull or use git merge in order to explicitly fast-forward, and you want to bail out if it can't fast-forward, then you can use the --ff-only flag. This way you can regularly do something like git pull --ff-only without thinking, and then if it errors out you can go back and decide if you want to merge or rebase.
Graphic answer to this question
Here is a site with a clear explanation and graphical illustration of using git merge --no-ff:
Until I saw this, I was completely lost with git. Using --no-ff allows someone reviewing history to clearly see the branch you checked out to work on. (that link points to github's "network" visualization tool) And here is another great reference with illustrations. This reference complements the first one nicely with more of a focus on those less acquainted with git.
Basic info for newbs like me
If you are like me, and not a Git-guru, my answer here describes handling the deletion of files from git's tracking without deleting them from the local filesystem, which seems poorly documented but often occurrence. Another newb situation is getting current code, which still manages to elude me.
Example Workflow
I updated a package to my website and had to go back to my notes to see my workflow; I thought it useful to add an example to this answer.
My workflow of git commands:
git checkout -b contact-form
(do your work on "contact-form")
git status
git commit -am "updated form in contact module"
git checkout master
git merge --no-ff contact-form
git branch -d contact-form
git push origin master
Below: actual usage, including explanations.
Note: the output below is snipped; git is quite verbose.
$ git status
# On branch master
# Changed but not updated:
# (use "git add/rm <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: ecc/Desktop.php
# modified: ecc/Mobile.php
# deleted: ecc/ecc-config.php
# modified: ecc/readme.txt
# modified: ecc/test.php
# deleted: passthru-adapter.igs
# deleted: shop/mickey/index.php
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# ecc/upgrade.php
# ecc/webgility-config.php
# ecc/webgility-config.php.bak
# ecc/webgility-magento.php
Notice 3 things from above:
1) In the output you can see the changes from the ECC package's upgrade, including the addition of new files.
2) Also notice there are two files (not in the /ecc folder) I deleted independent of this change. Instead of confusing those file deletions with ecc, I'll make a different cleanup branch later to reflect those files' deletion.
3) I didn't follow my workflow! I forgot about git while I was trying to get ecc working again.
Below: rather than do the all-inclusive git commit -am "updated ecc package" I normally would, I only wanted to add the files in the /ecc folder. Those deleted files weren't specifically part of my git add, but because they already were tracked in git, I need to remove them from this branch's commit:
$ git checkout -b ecc
$ git add ecc/*
$ git reset HEAD passthru-adapter.igs
$ git reset HEAD shop/mickey/index.php
Unstaged changes after reset:
M passthru-adapter.igs
M shop/mickey/index.php
$ git commit -m "Webgility ecc desktop connector files; integrates with Quickbooks"
$ git checkout master
D passthru-adapter.igs
D shop/mickey/index.php
Switched to branch 'master'
$ git merge --no-ff ecc
$ git branch -d ecc
Deleted branch ecc (was 98269a2).
$ git push origin master
Counting objects: 22, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (14/14), done.
Writing objects: 100% (14/14), 59.00 KiB, done.
Total 14 (delta 10), reused 0 (delta 0)
To git#github.com:me/mywebsite.git
8a0d9ec..333eff5 master -> master
Script for automating the above
Having used this process 10+ times in a day, I have taken to writing batch scripts to execute the commands, so I made an almost-proper git_update.sh <branch> <"commit message"> script for doing the above steps. Here is the Gist source for that script.
Instead of git commit -am I am selecting files from the "modified" list produced via git status and then pasting those in this script. This came about because I made dozens of edits but wanted varied branch names to help group the changes.
Merge Strategies
Explicit Merge (aka non fast-forward): Creates a new merge commit. (This is what you will get if you used --no-ff.)
Fast Forward Merge: Forward rapidly, without creating a new commit:
Rebase: Establish a new base level:
Squash: Crush or squeeze (something) with force so that it becomes flat:
The --no-ff option ensures that a fast forward merge will not happen, and that a new commit object will always be created. This can be desirable if you want git to maintain a history of feature branches.
In the above image, the left side is an example of the git history after using git merge --no-ff and the right side is an example of using git merge where an ff merge was possible.
EDIT: A previous version of this image indicated only a single parent for the merge commit. Merge commits have multiple parent commits which git uses to maintain a history of the "feature branch" and of the original branch. The multiple parent links are highlighted in green.
This is an old question, and this is somewhat subtly mentioned in the other posts, but the explanation that made this click for me is that non fast forward merges will require a separate commit.
The --no-ff flag causes the merge to always create a new commit object, even if the merge could be performed with a fast-forward. This avoids losing information about the historical existence of a feature branch and groups together all commits that together added the feature
Other answers indicate perfectly well that --no-ff results in a merge commit. This retains historical information about the feature branch which is useful since feature branches are regularly cleaned up and deleted.
This answer may provide context for when to use or not to use --no-ff.
Merging from feature into the main branch: use --no-ff
Worked example:
$ git checkout -b NewFeature
[work...work...work]
$ git commit -am "New feature complete!"
$ git checkout main
$ git merge --no-ff NewFeature
$ git push origin main
$ git branch -d NewFeature
Merging changes from main into feature branch: leave off --no-ff
Worked example:
$ git checkout -b NewFeature
[work...work...work]
[New changes made for HotFix in the main branch! Lets get them...]
$ git commit -am "New feature in progress"
$ git pull origin main
[shortcut for "git fetch origin main", "git merge origin main"]
What’s a fast-forward?
A fast-forward is what Git does when you merge or rebase against a branch simply ahead of the one you have checked-out.
Given the following branch setup:
You’ve got both branches referencing the same commit. They’ve both got precisely the same history. Now commit something to feature.
The master branch is still referencing 7ddac6c, while the feature has advanced by two commits. The feature branch can now be considered ahead of the master.
It’s now relatively easy to see what’ll happen when Git does a fast-forward. It simply updates the master branch to reference the exact commit that feature does. No changes are made to the repository itself, as the commits from the feature already contain all the necessary changes.
Your repository history would now look like this:
When doesn’t a fast-forward happen?
Fast-forwards don’t happen when changes have been made in the original branch and the new branch.
If you were to merge or rebase the feature onto master, Git would be unable to do a fast-forward because the trees have both diverged. Considering Git commits are immutable, there’s no way for Git to get the commits from feature into master without changing their parent references.
I have an existing repo which I connected to my gitlab via ssh
I am just trying to create my branch in sourcetree so that i push my changes there but in getting an error "not a valid object name master"
is there any part that i skipped because my created repository cant create new branches? thanks for reading any help will be great.
TLDR: created and linked via gitlab repo in sourcetree with an existing one, cant create branch to push
Check first if you are creating your branch from an empty repository: there should be at least one commit.
If the repository is not empty, check if:
your remote GitLab repository expects a default branch 'main'
your local repository uses master as its main branch
If it is the case, rename your local branch
cd /path/to/local/repository
git branch -m master main
And make sure any future local repository starts with main:
git config --global init.defaultbranch main
Try again your branch creation in SourceTree.
has been pulled, the result is still an error ! [rejected] dev->dev(non-fast-forward)
D:\PPI_Roketin\Project\astrowatch (dev)
λ git add resources/views/reals/_form.blade.php
D:\PPI_Roketin\Project\astrowatch (dev)
λ git commit -m "resources/views/reals/_form.blade.php"
[dev db5ac99] resources/views/reals/_form.blade.php
1 file changed, 2 insertions(+), 2 deletions(-)
D:\PPI_Roketin\Project\astrowatch (dev)
λ git push origin dev
To gitlab.com:roketin-stellar/astrowatch.git
! [rejected] dev -> dev (non-fast-forward)
error: failed to push some refs to 'git#gitlab.com:roketin- stellar/astrowatch.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
error like this, why?
have done git checkout, and repeated the command, it's still an error like above
This is a classic git error. The problem is described in the error message: Updates were rejected because the tip of your current branch is behind, which means that there have been changes to the remote branch that you do not have locally. There are quite a few ways to fix this, one of which is also described in the git message: Integrate the remote changes (e.g. hint: 'git pull ...') before pushing again.
If you do git pull origin dev this will merge whatever changes are made in the remote to your local branch. Once you fix the merge conflicts, you can then push the branch.
Another option would be to run a rebase by performing git pull --rebase origin dev, which will bring in the remote changes and rebase your commits on top of the remote commits. This will result in a cleaner git history.
The easiest for now would probably just doing a git pull, then once it's merged correctly, pushing to your remote.
I definitely recommend researching the git commands and learning how to find the resources correctly in the docs. There is a lot available online.
you can try git reset --hard HEAD
please remind that:
HEAD points to your current branch (or current commit), so all that git reset --hard HEAD will do is to throw away any uncommitted changes you have.
It's quite self explanatory, you need to keep yourself upto date with remote branch if you want to commit and it looks like git pull will overwrite files you've made changes to. You should commit your changes first.
I am using gitlab and really confused in few things. :
When we create new branch by git checkout -b test. Does it create copy of master or it creates copy of branch i am currently in?
For example: I am currently at branch dev, then i write command git checkout -b test. So it will be copy of dev, not masters?
Pull : when we write git pull , it pulls changes of current branch from remote branch of same name. Its used when more people are working on same project.
Example : I am at branch dev, i write git pull, it updates my local as of dev in remote. Now i created a new branch test, checkout test branch and do git pull. It gives me :There is no tracking information for the current branch.
Please specify which branch you want to rebase against.
its because there is no test branch in remote ?
What command to be used if i want to pull from dev branch while my current branch is test? is it git pull --rebase dev test?
When we write git push, it pushes current branch to remote one.
example : i am on branch test, i add, commit and write git push. It simply pushes my branch test to remote with same name as test.
Can we push to specific branch like push test to dev?
What is difference in following considering i am at branch test:
git push
git push origin test
they both push to remote?
My requirement is : there is branch dev which is not master branch, i am supposed to work on this branch as starting and end point. Like, new branch should be copy of this and i am supposed to push to same branch.
Does it create copy of master or it creates copy of branch i am currently in?
It creates a new branch named test based on the current branch, whatever that might be, as a starting point.
What command to be used if i want to pull from dev branch while my current branch is test?
I believe you can pull any branch you wish into your current branch. E.g. if you were on branch test and wanted to pull dev you could just use:
git pull origin dev
Can we push to specific branch like push test to dev?
Yes, can specify both the local and remote branch names when pushing, e.g.
git push origin test:dev
1- When you do git checkout -b test it creates copy of your current branch(in this case 'dev').
2- git pull will only sync your changes between remote and local. If you upload the branch and try to pull, it does not work because your local and remote changes will be synchronized.
3- This could help you : Make an existing Git branch track a remote branch?.
If you want to work in a copy of a branch, you should do this:
git checkout <origin_branch> (master, dev, what u want )
git checkout -b <work_branch> (test, for example. This create a copy of your origin branch)
After this, you have a new branch 'test' in your local repository. If you want to push this branch on the repo:
git add .
git commit -m "Pushing new branch test"
git --set-upstream origin <your_new_branch>
After checking out the branch of a colleague, at some point
I couldn't push or pull from remote.
Each time I ran git push or git push origin branch_name or those variant, I always got a everything up to date even though I had committed changes. Ultimately, what did the trick was git push -f origin HEAD:origin/branch_name. So my question is, why did I have to run this? what did I do to get there?
the fatal error I got was:
fatal: You are not currently on a branch.To push history leading to current (detached HEAD) state now, use: git push origin HEAD:<name-of-remote-branch>
Current State: Your head isn't attached to a branch, so you pushed with the branch whose history was the same as the remotes. You were working with a detached head, so when you pushed the head instead of the branch, it worked.
If you do a git status it with show:
HEAD detached at XXXXXXX
Fixing it:
Now that your remote has the changes you made, you can checkout the branch_name and pull your changes from remote. Your branch_name should already be tracking that remote branch.
Why you had to do this: You somehow detached your head. Without knowing the commands you used, we won't be able to tell you why you had to do this.
An example of how you could have done this:
#Don't do this, because force pulling will make you lose your work.
git checkout --detach
git pull --all -f #Don't do this.
You should probably edit (parts of) your last two ... er, now, three—comments into your question, but for now, I'll just quote them here:
I found where I might have off road. When I checked-out the branch I was going to work on, I got this message (let's called the branch b1:
Branch b1 set up to track remote branch b1 from origin.
Switched to a new branch 'b1'.
I checked-out the branch this way:
git checkout b1.
Then when I tried to push I did:
git push --set-upstream origin b1.
Branch b1 set up to track remote branch b1 from origin.
Everything up to date
OK, in that case you are probably fine.
git checkout
The problem you are running into here (well, in my opinion it's a problem) is that git checkout crams three or four (or more, depending on how you count) different things into one command.
Normally you would use:
git checkout somebranch
to check out (switch to) your local branch somebranch. Of course, that's great for branches you already have, but no good for branches you don't have locally yet. So Git crams another, different, command into git checkout: the "create new branch, then switch to it" command.
Normally this command is spelled git checkout -b newbranch. The -b flag means "create new". It's pretty reasonable to have this command spelled the same as git checkout, with just a flag, since we're switching to this newly created branch, but it probably would be clearer if it were a separate command, such as git create-and-then-switch-to-new-branch.
Now, pretty often, when you are creating a new local branch, you are doing so with the intent of having it "track" (as its "upstream") some existing remote-tracking branch, origin/b1 or whatever. In this case, you originally had to type in:
git checkout -b b1 --track origin/b1
which is still really obvious: create new branch b1, base it off remote-tracking branch origin/b1, and make it track (have as its upstream) origin/b1. Again, this might be clearer as git create-and-then-switch-to, but it's not so bad to have it shoved into git checkout, plus checkout is a lot shorter to type!
Now, many years ago (back in 2013 or so), the Git folks saw that this action was pretty common, and decided to make Git do it "magically" for you under certain conditions. If you write:
git checkout b1
and you don't currently have a branch b1 and you do currently have an origin/b1 and you don't have any other remote-tracking branch whose name ends in b1, then (and only then!), git checkout uses the create-and-then-switch-to-new-branch command, with the --track flag. So:
git checkout b1
means
git checkout -b b1 --track origin/b1
in this one special (but common) case.
--track means "set upstream"
Each branch (well, each named, local, branch) can have one (1) "upstream" set. The upstream of a branch has a bunch of uses: git status, git fetch, git merge, git rebase, and git push all look at the upstream. Having an upstream set makes git status show how many commits you are ahead and/or behind of the current branch's upstream. And, it lets you run all the other commands with no additional arguments: they can figure out where to fetch from, or what to merge or rebase, or what to push, using the current branch's name and its upstream setting.
Branches do not have to have an upstream, and a new branch you create doesn't, by default. Moreover, if it's a "very new" branch—one that does not exist on origin, for instance—there's no origin/name yet to have as an upstream in the first place. But when you create a new branch from a remote-tracking branch, setting the remote-tracking branch as the upstream of the new branch is almost always the right thing.
(Note: having an upstream also affects git pull because git pull is just git fetch followed by either git merge or git rebase. Until they are very familiar with Git, I think most people are noticeably better off running git fetch first, then git rebase or git merge second, depending on which one they want—and more often, that's actually git rebase, which is not the default for git pull: git pull defaults to using git merge second. Once they are familiar with the fetch-and-whatever sequence, then people can configure git pull to do the one they intend, and use git pull as a shortcut to run both commands. However, there are times to keep them separate anyway.)
For completeness
The other things that git checkout can do, that don't change the current branch, are:
copy files from the index to the work-tree: git checkout -- <paths>, including the special git checkout --ours and git checkout --theirs variations
copy files from specified commits to the index and then on to the work-tree: git checkout <tree-ish> -- <paths>
restore conflicted merge files to their conflicted state: git checkout -m -- <paths> (as with --ours and --theirs, this only works during a conflicted merge)
interactively patch files: git checkout -p (this variant gets especially complicated and I'm not going to address it further)
The checkout command can also "detach HEAD" (see previous answer below) and create "orphan" branches, but both of these effectively change your current branch, so they don't fall into this special "non-branch-changing" class of checkout operations.
So what happened with the first git push
When you ran:
git push --set-upstream origin b1
you told your Git to contact the other Git on origin, give it the commit ID to which your branch b1 pointed, see if it needed any of those commits and files (it didn't), and then ask it to set its branch name b1 to point to the same commit.
Now, you'd just created your own local b1 from your origin/b1, which you recently git fetch-ed from origin, so your b1 already pointed to the same commit (you did not make any new commits) and hence your git push gave them their own commit hash back and they said "Well, gosh, that's what I already have! We're all up-to-date!" And then their Git and your Git said goodbye to each other, and your Git carried out your last instruction: --set-upstream.
This changed the upstream of your local branch b1 to origin/b1. Of course, this was already the upstream for your local branch b1, so that was not a big change. :-) But your Git did it anyway, and then reported that everything was still up to date.
Earlier answer (was mostly done when you added your third comment)
... I had one file committed with changes. git status didn't return nothing after I committed.
git status should always print something, such as (two actual examples):
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working directory clean
or:
HEAD detached at 08bb350
nothing to commit, working directory clean
In this second case, you are in "detached HEAD" mode. This special mode means you are not on a branch—or it may be more accurate to say that you are on the (single, special-case) anonymous branch that goes away as soon as you get on any other branch.
Any commits you make while in detached HEAD mode are perfectly fine, normal commits—except that there is no branch name that will make them permanent. This means these commits can go away once you switch to some other branch.
All my attempts to push were unsuccessful.
Again, actual git push output might help here, so one could tell why the push failed or did nothing. If it complained about being in "detached HEAD" mode, that would be significant: we would know that you were in the detached HEAD mode.
git fetch -a didn't return nothing as well.
(Just an aside: It's a bit odd to use -a here: that flag is almost never useful to humans and is intended more for scripting purposes. It just makes your fetch append to FETCH_HEAD rather than overwriting it. The special FETCH_HEAD name is meant for scripts to use.)
the last step I had were to git checkout origin/branch_name.
This will take you off whatever branch you were on before, and put you in "detached HEAD" mode. If you used to be in "detached HEAD" mode, it will abandon your previous anonymous branch and put you on a new, different anonymous branch.
Any commits you made while on the previous anonymous branch are ... well, not lost, precisely, but are now difficult to find (and set to expire after 30 days).
When I tried to push from there I got the following message:
fatal: You are not currently on a branch.
To push history leading to current (detached HEAD) state now, use:
git push origin HEAD:<name-of-remote-branch>.
Which I did [... HEAD:origin/branch_name] and it worked
Yes, but alas, this did nothing useful, because you are now on an anonymous branch that is exactly the same as origin/branch_name. This ends up asking the remote to create—on the remote, as a local branch there—the name origin/branch_name. That's probably not a good idea, since having a local branch named origin/branch_name is like having a bicycle named "motorcycle". If you also have a motorcycle and ask your friend to bring you "motorcycle", which will he bring you, the bicycle named "motorcycle", or the motorcycle?