From 5065ce412ef083a02288c1972ea3d07423cace0e Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sun, 20 Sep 2020 23:22:29 +0000 Subject: [PATCH 1/3] docs: explain why squash merges are broken with long-running branches In many projects, squash merges are commonly used, primarily to keep a tidy history in the face of developers who do not use logically independent, bisectable commits. As common as this is, this tends to cause significant problems when squash merges are used to merge long-running branches due to the lack of any new merge bases. Even very experienced developers may make this mistake, so let's add a FAQ entry explaining why this is problematic and explaining that regular merge commits should be used to merge two long-running branches. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- Documentation/gitfaq.txt | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/Documentation/gitfaq.txt b/Documentation/gitfaq.txt index 9cd7a592ac..51d305d58f 100644 --- a/Documentation/gitfaq.txt +++ b/Documentation/gitfaq.txt @@ -241,6 +241,38 @@ How do I know if I want to do a fetch or a pull?:: ignore the upstream changes. A pull consists of a fetch followed immediately by either a merge or rebase. See linkgit:git-pull[1]. +Merging and Rebasing +-------------------- + +[[long-running-squash-merge]] +What kinds of problems can occur when merging long-lived branches with squash merges?:: + In general, there are a variety of problems that can occur when using squash + merges to merge two branches multiple times. These can include seeing extra + commits in `git log` output, with a GUI, or when using the `...` notation to + express a range, as well as the possibility of needing to re-resolve conflicts + again and again. ++ +When Git does a normal merge between two branches, it considers exactly three +points: the two branches and a third commit, called the _merge base_, which is +usually the common ancestor of the commits. The result of the merge is the sum +of the changes between the merge base and each head. When you merge two +branches with a regular merge commit, this results in a new commit which will +end up as a merge base when they're merged again, because there is now a new +common ancestor. Git doesn't have to consider changes that occurred before the +merge base, so you don't have to re-resolve any conflicts you resolved before. ++ +When you perform a squash merge, a merge commit isn't created; instead, the +changes from one side are applied as a regular commit to the other side. This +means that the merge base for these branches won't have changed, and so when Git +goes to perform its next merge, it considers all of the changes that it +considered the last time plus the new changes. That means any conflicts may +need to be re-resolved. Similarly, anything using the `...` notation in `git +diff`, `git log`, or a GUI will result in showing all of the changes since the +original merge base. ++ +As a consequence, if you want to merge two long-lived branches repeatedly, it's +best to always use a regular merge commit. + Hooks ----- From 409f066716598d5050c34b7bb0e6859940428dcf Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sun, 20 Sep 2020 23:22:30 +0000 Subject: [PATCH 2/3] docs: explain why reverts are not always applied on merge A common scenario is for a user to apply a change to one branch and cherry-pick it into another, then later revert it in the first branch. This results in the change being present when the two branches are merged, which is confusing to many users. We already have documentation for how this works in `git merge`, but it is clear from the frequency with which this is asked that it's hard to grasp. We also don't explain to users that they are better off doing a rebase in this case, which will do what they intended. Let's add an entry to the FAQ telling users what's happening and advising them to use rebase here. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- Documentation/gitfaq.txt | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Documentation/gitfaq.txt b/Documentation/gitfaq.txt index 51d305d58f..176b097cf7 100644 --- a/Documentation/gitfaq.txt +++ b/Documentation/gitfaq.txt @@ -273,6 +273,27 @@ original merge base. As a consequence, if you want to merge two long-lived branches repeatedly, it's best to always use a regular merge commit. +[[merge-two-revert-one]] +If I make a change on two branches but revert it on one, why does the merge of those branches include the change?:: + By default, when Git does a merge, it uses a strategy called the recursive + strategy, which does a fancy three-way merge. In such a case, when Git + performs the merge, it considers exactly three points: the two heads and a + third point, called the _merge base_, which is usually the common ancestor of + those commits. Git does not consider the history or the individual commits + that have happened on those branches at all. ++ +As a result, if both sides have a change and one side has reverted that change, +the result is to include the change. This is because the code has changed on +one side and there is no net change on the other, and in this scenario, Git +adopts the change. ++ +If this is a problem for you, you can do a rebase instead, rebasing the branch +with the revert onto the other branch. A rebase in this scenario will revert +the change, because a rebase applies each individual commit, including the +revert. Note that rebases rewrite history, so you should avoid rebasing +published branches unless you're sure you're comfortable with that. See the +NOTES section in linkgit:git-rebase[1] for more details. + Hooks ----- From 087c61677c358091692ac6ce1de00bbfc9affdb1 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sun, 20 Sep 2020 23:22:31 +0000 Subject: [PATCH 3/3] docs: explain how to deal with files that are always modified Users frequently have problems where two filenames differ only in case, causing one of those files to show up consistently as being modified. Let's add a FAQ entry that explains how to deal with that. In addition, let's explain another common case where files are consistently modified, which is when files using a smudge or clean filter have not been run through that filter. Explain the way to fix this as well. Signed-off-by: brian m. carlson Signed-off-by: Junio C Hamano --- Documentation/gitfaq.txt | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/Documentation/gitfaq.txt b/Documentation/gitfaq.txt index 176b097cf7..afdaeab850 100644 --- a/Documentation/gitfaq.txt +++ b/Documentation/gitfaq.txt @@ -363,6 +363,39 @@ information about how to configure files as text or binary. You can also control this behavior with the `core.whitespace` setting if you don't wish to remove the carriage returns from your line endings. +[[always-modified-files-case]] +Why do I have a file that's always modified?:: + Internally, Git always stores file names as sequences of bytes and doesn't + perform any encoding or case folding. However, Windows and macOS by default + both perform case folding on file names. As a result, it's possible to end up + with multiple files or directories whose names differ only in case. Git can + handle this just fine, but the file system can store only one of these files, + so when Git reads the other file to see its contents, it looks modified. ++ +It's best to remove one of the files such that you only have one file. You can +do this with commands like the following (assuming two files `AFile.txt` and +`afile.txt`) on an otherwise clean working tree: ++ +---- +$ git rm --cached AFile.txt +$ git commit -m 'Remove files conflicting in case' +$ git checkout . +---- ++ +This avoids touching the disk, but removes the additional file. Your project +may prefer to adopt a naming convention, such as all-lowercase names, to avoid +this problem from occurring again; such a convention can be checked using a +`pre-receive` hook or as part of a continuous integration (CI) system. ++ +It is also possible for perpetually modified files to occur on any platform if a +smudge or clean filter is in use on your system but a file was previously +committed without running the smudge or clean filter. To fix this, run the +following on an otherwise clean working tree: ++ +---- +$ git add --renormalize . +---- + [[recommended-storage-settings]] What's the recommended way to store files in Git?:: While Git can store and handle any file of any type, there are some