4

What Git commands will display content present only in a branch whose commits have been fully merged to master? Some content may be missing from master because, for example some merge operations may have used the ours strategy, ignoring the branch content (i.e. git merge --strategy ours branch).

I have found that the usual Git comparison operations do not produce the result that I desire.

For example, commands git diff master..branch and git diff branch..master do not work because these show content differences present in master. (How can I modify this command to omit the changes on master?)

Command git diff master...branch also does not work because all commits on branch have previously been merged to master in multiple merge operations over time.

Example:

  1. Create file a.txt in branch master.
  2. Add line 1 to a.txt in branch master.
  3. Create branch branch.
  4. Add line 2 to a.txt in branch branch.
  5. Switch to branch master.
  6. Add line 2 to a.txt in branch master.
  7. Merge branch branch to master using strategy ours. This is like a "dummy" merge which merges all commits on branch to master, but discards their content.
  8. Compare branches master and branch.

Command sequence:

derek@derek-lubuntu:~/Projects$ git init test
Initialized empty Git repository in /home/derek/Projects/test/.git/
derek@derek-lubuntu:~/Projects$ cd test
derek@derek-lubuntu:~/Projects/test$ touch a.txt
derek@derek-lubuntu:~/Projects/test$ git add a.txt
derek@derek-lubuntu:~/Projects/test$ git commit --message "Added a.txt."
[master (root-commit) 6a36816] Added a.txt.
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 a.txt
derek@derek-lubuntu:~/Projects/test$ echo "Line 1 from master." >> a.txt
derek@derek-lubuntu:~/Projects/test$ git commit -a --message "Added line 1."
[master c9ebf16] Added line 1.
 1 file changed, 1 insertion(+)
derek@derek-lubuntu:~/Projects/test$ git checkout -b branch
Switched to a new branch 'branch'
derek@derek-lubuntu:~/Projects/test$ echo "Line 2 from branch." >> a.txt
derek@derek-lubuntu:~/Projects/test$ git commit -a --message "Added line 2."
[branch 8a142dd] Added line 2.
 1 file changed, 1 insertion(+)
derek@derek-lubuntu:~/Projects/test$ cat a.txt
Line 1 from master.
Line 2 from branch.
derek@derek-lubuntu:~/Projects/test$ git checkout master
Switched to branch 'master'
derek@derek-lubuntu:~/Projects/test$ echo "Line 2 from master." >> a.txt
derek@derek-lubuntu:~/Projects/test$ git commit -a --message "Added line 2."
[master d496cc7] Added line 2.
 1 file changed, 1 insertion(+)
derek@derek-lubuntu:~/Projects/test$ git merge --strategy ours branch
Merge made by the 'ours' strategy.
derek@derek-lubuntu:~/Projects/test$ git merge branch
Already up-to-date.
derek@derek-lubuntu:~/Projects/test$ git diff master..branch
diff --git a/a.txt b/a.txt
index f773e76..5dae91a 100644
--- a/a.txt
+++ b/a.txt
@@ -1,2 +1,2 @@
 Line 1 from master.
-Line 2 from master.
+Line 2 from branch.
derek@derek-lubuntu:~/Projects/test$ git diff branch..master
diff --git a/a.txt b/a.txt
index 5dae91a..f773e76 100644
--- a/a.txt
+++ b/a.txt
@@ -1,2 +1,2 @@
 Line 1 from master.
-Line 2 from branch.
+Line 2 from master.
derek@derek-lubuntu:~/Projects/test$ git diff master...branch
derek@derek-lubuntu:~/Projects/test$ git diff branch...master
diff --git a/a.txt b/a.txt
index 5dae91a..f773e76 100644
--- a/a.txt
+++ b/a.txt
@@ -1,2 +1,2 @@
 Line 1 from master.
-Line 2 from branch.
+Line 2 from master.

What Git operation can I use to show only "+Line 2 from branch." while ignoring "+Line 2 from master." or "-Line 2 from master."?

Community
  • 1
  • 1
Derek Mahar
  • 27,608
  • 43
  • 124
  • 174
  • Have you tried `git diff master branch`? – neshkeev Sep 08 '15 at 16:44
  • Isn't this identical to `git diff master..branch`? – Derek Mahar Sep 08 '15 at 16:45
  • I don't understand your problem, I just simulated a conflict and solve it in the master. Then I execute this command and got difference in master: I have some content and in the other branch I have different content. – neshkeev Sep 08 '15 at 16:52
  • @zaratustra I think OP wants the command to ignore additions to `master` that are not in `branch`. `git diff master branch` would show additions/deletions both ways (and idk of any diff tool that wouldn't end up doing that). – SnoringFrog Sep 08 '15 at 18:06
  • @zaratustra, I added an an example to my question to illustrate my problem. – Derek Mahar Sep 08 '15 at 18:09
  • @SnoringFrog, yes, you correctly understand my problem. – Derek Mahar Sep 08 '15 at 18:17
  • This would be useful, for example, to tell you which changes in a branch may be absent from `master` even after having merged all of the commits on the branch to `master`. – Derek Mahar Sep 08 '15 at 19:02

2 Answers2

0

Drawing from How do I do a one way diff in Linux?:

You can pipe the output off the diff to grep to sort out the changes present in master. Best way I know to do that is to ignore any line that starts with a hyphen.

git diff master branch | grep -v "^-"

For me this showed the following output after following your steps:

diff --git a/a.txt b/a.txt

index f908dc1..1b5b3bd 100644

+++ b/a.txt

@@ -1,2 +1,2 @@

this line in both files

+line2 on branch

whereas git diff master branch shows

diff --git a/a.txt b/a.txt

index f908dc1..1b5b3bd 100644

--- a/a.txt

+++ b/a.txt

@@ -1,2 +1,2 @@

this line in both files

-line2 in master

+line2 on branch

This has the side effect of stripping the color from the diff and printing it out directly to stdout instead of to less. Since the color is there to help differentiate which file each listed change is in, it should not be necessary in one files changes removed. If you need to keep things displayed in less, simply add a pipe to less to the end of the command.

Edit: As @DerekMahar pointed out in his comment, this only shows you additions on branch that are not reflected in master. I'm not sure how to modify this to include all changes from branch, as I know no simple way to differentiate between an addition to master and a subtraction from branch while viewing a diff.


The below is a very rough idea that, as explained here, would not fully solve the problem, but it feels like enough of a start I thought I should note it in case someone else knows how to work around the issues with it:

My best initial thought (which requires more scripting than I have time for right now, and also has it's own collection of issues, but I figured I'd put it out here in case someone else can work with it) is to take the output of git diff master branch | grep "^-" (notice the lack of -v), and then search for the lines of that output in the proper file in branch (the filename would be available in the first line of output). We would then know any lines found in branch were removed from master, while any lines not found were added to master. However, lines that may be repeated (e.g., { by itself) would quickly cause problems with this method, and I'm not sure how to resolve that issue.

Community
  • 1
  • 1
SnoringFrog
  • 1,479
  • 1
  • 16
  • 30
  • This may work for the simple example that I gave, but in another scenario, it also has the side effect of omitting lines that `branch` may have deleted. It also doesn't ignore files that were added only to `master`. – Derek Mahar Sep 08 '15 at 18:32
  • @DerekMahar In that case I think you're out of luck, as there's no way to differentiate between "deleted from `branch`" and "added to `master`" (even when looking at a normal diff, as far as I can tell. Much less on a one-way diff like this), as both just result in text in `master` that is absent from `branch`. – SnoringFrog Sep 08 '15 at 18:45
  • SnoringFrog, you may be right, but the algorithm wouldn't be too difficult. Git would have to calculate the difference between all changes on `branch` and the changes that actually resulted from their merge into `master` while ignoring all new changes on `master`. – Derek Mahar Sep 08 '15 at 18:55
  • 1
    Maybe this would be more difficult to implement than I first thought. – Derek Mahar Sep 08 '15 at 18:58
  • @DerekMahar My best initial thought (which requires more scripting work than I can do right now) is to take all the lines starting with "-" in the diff, store them, then search for each in `branch`. Anything found was removed from master, anything not found was added to master. You'd have to work with how to reformat and search for the lines effectively. Order would have to be considered though, otherwise repeated lines would create numerous issues. – SnoringFrog Sep 08 '15 at 19:07
0

Applying the algorithm to which I referred in my earlier comment:

  1. Calculate all changes on branch since it branched from master using command git diff $(git merge-base master branch)^ branch. In the given example, $(git merge-base master branch)^ refers to the parent of the first commit on branch.
  2. Apply these changes to master by piping them into patch -p1.
  3. Calculate the difference using git diff.

For example:

derek@derek-lubuntu:~/Projects/test$ git diff $(git merge-base master branch)^ branch | patch -p1
patching file a.txt
Hunk #1 succeeded at 1 with fuzz 1.
derek@derek-lubuntu:~/Projects/test$ git diff
diff --git a/a.txt b/a.txt
index f773e76..de87c33 100644
--- a/a.txt
+++ b/a.txt
@@ -1,2 +1,3 @@
 Line 1 from master.
+Line 2 from branch.
 Line 2 from master.
derek@derek-lubuntu:~/Projects/test$ 

Though this doesn't omit the changes applied to master, it does prevent git diff from reporting them as changes.

Note that step 1 would work only for cases where a branch is merged only once, which is not my case. Instead, we could tag the original branch commit, which fortunately, I have done.

Community
  • 1
  • 1
Derek Mahar
  • 27,608
  • 43
  • 124
  • 174