I have fetched origin, and I see the following when doing git diff origin/master:
diff --git a/flash/controllers b/flash/controllers
deleted file mode 120000
index 350cadb..0000000
--- a/flash/controllers
+++ /dev/null
@@ -1 +0,0 @@
-/home/testapp/www/homedir/flash/controllers
\ No newline at end of file
I am confused on what does this mean. Does this mean that the file will be deleted if I do a merge? It's confusing because I have /flash/controllers/ in gitignore.
TL;DR version: while everything below is good to know, the easiest way to see what a merge will do is to do the merge. Just make sure everything is clean (git status does not have anything for you to check in, for instance), then run the merge and see what it did. If you don't like the result, abort the merge (if it's failed and needs help) or back it out (if it succeeded but you just don't like the result) with one of:
git merge --abort # after merge fails and asks you for help
or:
git reset --hard HEAD^ # after merge succeeds but you don't like the result
Deleted means that when comparing two trees-of-files, the tree on the "left side" of the comparison had something, and the tree on the "right side" no longer has it.
The mode identifies the type of a file-object, and 120000 specifically means "a symbolic link". So this means that comparing version a to version b, there used to be a symbolic link named flash/controllers in version a, and in version b, there isn't anymore.
If you just run git diff origin/master, the tree on the left (tree a) is your current working directory (not a specific commit!), and the tree on the right (tree b) is the one on the commit identified by origin/master (use git rev-parse origin/master to see the actual SHA-1 of this commit). If you name two commits, as in git diff master origin/master for instance, the tree on the left is the one named by the first SHA-1 (again, use git rev-parse to see raw SHA-1s) and the tree on the right is the one named by the second.
Does this mean that the file will be deleted if I do a merge?
Maybe; maybe not. What determines if the file will be deleted by a merge is the difference between the merge-base and the items being merged.
Let's say, for instance, that a above represents the tree at the tip of your current branch, probably named master, and b represents the tree at the tip of your branch named origin/master (this is a "remote branch", but it's really a branch in your repository; you've just copied it from origin).
What we don't know is the merge-base between master and origin/master. That's going to be some commit—we just don't know which one.
Let's say the commit graph, if we were to draw it out, looks like this:
...-o-o-B-u <-- HEAD=master
\
t <-- origin/master
Here, you made one commit on your master branch. I labeled this u, for us. Meanwhile "they" (whoever they are) made one commit on their branch, which they call master and which you are calling origin/master; I labeled this commit t, for them. The common starting point—the point where you and they diverged—is the commit labeled I have labeled B here.
You can find this commit's SHA-1 ID with the git merge-base command:
git merge-base HEAD origin/master
If you do:
git diff -M --name-status $(git merge-base HEAD origin/master) origin/master
(this assumes an sh or bash style command line),1 git will tell you what files "they" added, modified, deleted, and renamed, in their commit t. That is, this compares commit B against the tip of origin/master, which is commit t.
If you compare B against the tip of your current branch, i.e., against the HEAD commit, you'll see what you did in your commit u.
If you then do:
git merge origin/master
git will attempt to merge "what you did" and "what they did". Here's the key to what happens to the symbolic link, or for that matter any file. There are three possibilities in general, although only two are likely:
If the symbolic link existed in B, then you probably did nothing, and they deleted it. In this case, git merge will delete it: you did nothing, they deleted it, combining these is easy: delete it.
If the symbolic link did not exist in B, then you added it, and they probably did nothing. In this case, git merge will add (well, keep) it. Again, this is easy to combine: you added, they did nothing, and the combination of the two is "add" (or really, "keep").
If the symbolic link existed in B, but you changed it, and they deleted it, git merge will declare a conflict and make you resolve the situation yourself. This is not easy for git to combine, in the same way that it does not know what to do if you and they both changed it but in different ways.2
(We know, or believe anyway based on the original git diff output, that the tree associated with commit HEAD has the symbolic link, and the tree associated with commit origin/master does not, so we don't have to worry about the "both created" or "both modified" cases.)
It's confusing because I have
/flash/controllers/in gitignore.
But that's flash/controllers/, with a trailing slash (the leading slash is not that important either way, I think—though the ignore rules still confuse me sometimes). The gitignore documentation includes this bit:
• If the pattern ends with a slash, it is removed for the purpose of the following description, but it would only find a match with a directory. In other words, foo/ will match a directory foo and paths underneath it, but will not match a regular file or a symbolic link foo (this is consistent with the way how pathspec works in general in Git).
(There's also the issue that if a file is already tracked, adding it to .gitignore does not make it untracked. The .gitignore contents are merely used to keep files from becoming tracked accidentally, and to avoid git status complaining about them.)
1In fact, getting a diff against the merge-base has a special git diff syntax, repurposing the three-dot A...B syntax from gitrevisions. To find the merge-base between A and B and compare it to the contents of commit B, you can write git diff A...B. As always, omitting one end is equivalent to specifying HEAD, so git diff ...origin/master compares the merge-base (of HEAD and origin/master) to origin/master; and git diff origin/master... compares the merge-base (of the same two commits) to HEAD. Add -M to detect renames and you have the comparisons that git merge uses.
(I'm deliberately omitting some extra details with "recursively defined" virtual merge bases here. In the case of two "equally valid" merge base ancestors, git diff will pick one arbitrarily, but git merge with the recursive strategy will construct a virtual merge-base instead. These get a bit complicated.)
2For most regular files, if you and they have both modified them, git attempts to combine the changes by deciding which changes are independent (git keeps one copy of each such change) and which changes are identical (git keeps one copy of those changes as well, rather than putting two copies in). Whether and when git is actually correct about this—specifically, about which changes are "the same" and which are "different"—is another matter. It usually does pretty well, but it's just a computer algorithm and it sometimes goes awry, so you, the user, must check the result in some way (e.g., "does it still build, does it pass tests, does it look OK", whatever tests you have), to be really sure about it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With