So I am having a little trouble understanding Git. I'm using it on Windows, with a remote (GitLab installed) repo. I have a local copy of the master branch, and I am fairly diligent about using git pull
frequently to keep my local copy up-to-date.
So it's release time (of course...isn't that when this happens?) and I want to check in the ONE file I've changed. I type a git pull
but get an error:
error: Your local changes to the following files would be overwritten by merge:
<path>/filename.py
Please, commit your changes or stash them before you can merge.
Aborting
I use git diff
and sure enough, another developer fixed a typo in the same spot where I'm trying to commit. A quick git status
reveals:
Your branch is behind 'origin/master' by 4 commits, and can be fast-forwarded
So, at a loss, I commit my changes to my local repo.
git add <filename>
git commit -m "reworking to ignore logs"
Now, I can git pull
successfully.
git pull origin master
...
Merge made by the 'recursive' strategy
file1
file2...
....
<filename>
5 files changed, 1 insertion, 63 deletions...
Now, I'm able to push my change up to the remote repo
git push
BUT: here's my question. On the remote server, it shows BOTH my commit AND the file changes caused by my local pull
after my commit. That is to say, it basically shows that the remote files changed, but in fact changed to what wass ALREADY in the remote repository.
My boss calls in a panic, asking me why I changed all those (other four) files, and I'm not able to answer. We end up checking out the two commits (current and pre-my-changes) and diffing them, to prove that it was ONLY my one file's change is what is different, before we both breathe a sigh of relief.
So my question is: what am I missing? Why should Git think that I want to see that I changed files to match the remote HEAD, in the remote? I have seen this question about git squash
ing, and realize that there is a git rebase
which may help me, too. Can someone help me understand why Git does this? I just feel as though I am not totally embracing the "distributed" paradigm shift.
Apparently both you and your boss are missing the same thing, and your boss tends towards panic. :-)
Here's what you did, in graphical form drawn as commits. Originally you had this in your repo:
... - F - G <-- HEAD=master, origin/master
where G
is the then-latest version. But there's a typo in one file, so you edited that file in your work-tree, fixing the problem. (I'm going to name the file typo
just for convenience below.)
$ vim typo
...
You haven't committed this yet, but you did change it. Then you did:
$ git pull
which got you that error message.
Now, behind the scenes, as it were, git pull
is just git fetch
followed by git merge
. So let's look at what git fetch
did. It seems that other developers made four commits; let's draw them into the graphical representation of the commit-tree:
H - I - J - K <-- origin/master
/
... - F - G <-- HEAD=master
The git merge
failed because someone else modified file typo
too.
So, now you do:
$ git add typo
$ git commit -m "reworking to ignore logs"
This adds a new commit on your current branch (whatever HEAD
points to), which is obviously master
. This gives a commit tree that looks like this:
H - I - J - K <-- origin/master
/
... - F - G ------------- L <-- HEAD=master
Now you run git pull
again, which again is just fetch
followed by merge
. The fetch has nothing to fetch this time (commits H
through K
, and the label origin/master
, are already all taken care of). The merge then merges commit K
(origin/master
) into your current branch, master
, creating a new merge commit M
:
H - I - J - K <-- origin/master
/ \
... - F - G ------------- L - M <-- HEAD=master
If you now git push
the result, commit M
goes in, merging your change, L
, as the first parent of the merge, and their commit K
as the second parent.
That's the actual history of what happened to produce M
(as hobbs noted in comment) and is perfectly normal. However, you (and your panicky boss) might prefer to construct a different history, that pretends you noticed the problem in typo
after you picked up commits H
through K
. To do that, you'd "rebase" your commit (L
, above) to sit on top of origin/master
.
You can do this with git pull --rebase
(as in hobbs' answer that appeared while I was typing up this long one), or with plain old git rebase
after doing a git fetch
. In this case, what you will do is make a copy of the changes in commit L
(people often spell this "commit L'
"):
H - I - J - K - L'
/
... - F - G ------------- L
At this point, you don't need commit L
any more—it will stick around in the repo for 90 days or so because of git's "reflogs", but it will vanish from normal git log
output, for instance—so you just need to re-draw this graph a bit, leaving out the line going to L
, and stick the labels on like so:
H - I - J - K <-- origin/master
/ \
... - F - G L' <-- HEAD=master
and this is exactly what the git rebase
in the command-sequence:
$ git add typo
$ git commit -m "reworking to ignore logs"
$ git rebase
would have done. (Or, again, you can commit and then use git pull --rebase
, which just makes git use the fetch
-then-rebase
sequence, instead of fetch
-then-merge
. If there's nothing to fetch, as in this case, that's the same as just running git rebase
.)
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments