Git merge- will old problems be merged into master if I merge an old branch?

408 views Asked by At

I am using Git as the version control for my development, and am relatively new to it.

Shortly after starting work on this project, I created a branch called updateCards to work on resolving a particular bug within the project.

However, while working on this, and before I had pushed my changes to the server, several other bugs were raised that required more urgent attention. As a result, I committed my changes to updateCards, and then switched to a new branch for each of these other more pressing bugs.

I have resolved these other bugs, merged the branches I created for them into master and pushed my changes to the server for each of them.

I now want to go back to the old updateCards branch, merge that with master & push to the server. When I view the project from the updateCards branch, I can see that the bug that this branch was created for has been resolved, so I'm happy that I am ready to push it to the server.

However, what I'm unsure about is, given that I have made several other changes to master on the server since creating updateCards, if I merge updateCards to master now, will I merge any of the old bugs that were existing in updateCards but are now resolved in master back to master, since the files where these bugs have been fixed on master will be different to those same files on updateCards? Or will Git see that the changes on master for these files are more recent than the changes on updateCards, and so not merge those changes?

I ran a git diff master..updateCards, and this has displayed output about the differences between the two branches:

diff --git a/buying/templates/buying/update_card_numbers.html b/buying/templates/buying/update_card_numbers.html
index 6cc5938..5f6a8f3 100644
--- a/buying/templates/buying/update_card_numbers.html
+++ b/buying/templates/buying/update_card_numbers.html
@@ -25,8 +25,8 @@
            <table class="left">
                    <thead>
                            <tr>
-                                       <th>Cardholder</th>
                                    <th>card no</th>
+                                       <th>Cardholder</th>
                            </tr>
                    </thead>

diff --git a/buying/views.py b/buying/views.py
index 08d2fd6..c777020 100644
--- a/buying/views.py
+++ b/buying/views.py
@@ -1555,6 +1555,8 @@ def update_card_numbers(request):
                                            cardholder = data['id']
                                            cardholder.card_no = data['card_no']
                                            cardholder.save()
+                                               #cardholder.card_no.save()
+                                               #data['travis_card_no'].save()
                                            print cardholder, cardholder.card_no

                            HttpResponseRedirect(reverse('buying:update_card_numbers'))
diff --git a/costing/templates/pdf2_base.html b/costing/templates/pdf2_base.html
index 3826a98..c139068 100644
--- a/costing/templates/pdf2_base.html
+++ b/costing/templates/pdf2_base.html
@@ -83,8 +83,6 @@
                                            <td>
                                                    <span class="project-name">{{project.project_name|upper}}</span>
                                            </td>
-                                               <!--ERF(07/12/2016 @ 1615) Display today's date in the header -->
-                                               <td> {% date_to_display %}</td>
                                    </tr>
                            </table>
                    </div>  
diff --git a/costing/views.py b/costing/views.py
index 902f9ff..f8a3f77 100644
--- a/costing/views.py
+++ b/costing/views.py
@@ -2438,9 +2438,6 @@ def pdf2_master(request, project_id):
            """ Save to the current budget (no version number), as versions not used once deposit is received """
            budget = get_current_budget(project_id)

-               #ERF(07/12/2016 @ 1615) Create a date variable to displays today's date on the PDF when it's generated
-               date_to_display = datetime.now()
-
            if not budget:
                    Budget.objects.create(project=project, current_marker=1)

But I'm not sure how to interpret this output... Are the lines beginning with - something that exists in updateCards, but not in master, and the lines beginning with + something that exists in master, but not in updateCards, or vice versa?

Which changes will be copied in which direction if I run a merge?

2

There are 2 answers

0
torek On

You can never anticipate a merge from one diff

To see what a merge will do, you need two diffs.1

You also need to find the merge base (or have Git do it for you). This is because merging is about combining changes, and to combine two peoples' changes, we have to find the common place from which they both started. That starting-point allows us (or Git) to figure out "what we did" and "what they did".

You may then run your two diffs: git diff merge-base tip1 and git diff merge-base tip2. Doing the merge—this is "merge" as a verb—will then combine those two diffs into one big change that applies to the merge base.

Equivalently, you can think of this as: Find things in the second diff that aren't already in the first diff, and add them to the result of applying the first diff, i.e., to tip1. This assumes you're going to make the new merge as a commit that extends tip1.

For instance, if you are on branch main and you're merging in branch fix, tip1 is whatever commit-ID main resolves to, and tip2 is whatever commit-ID fix resolves to. Once the merge (as a verb) finishes, you'll make a new merge-commit—this is "merge" as an adjective—on main, so that main now names the new merge commit you just made. The contents of the new merge ("merge" as a noun, meaning "merge commit") are made by the merge-as-a-verb process, that combines the two diffs.


1In some reasonably rare, complicated cases, you need more than two, but we'll ignore them here.


If that was confusing, I'm not sure the rest will help. But there's a reward at the end: a quick way to run git diff to get the two diffs you need. :-)

Definitions

Since you're new to Git, let's define a few items. They're probably familiar enough but it's good to have specific definitions.

A tree is a collection of files. The tree you work on is your work tree (also spelled work-tree, working tree, etc). Your work tree can have files that don't get committed: these are untracked files. You can have Git "ignore" (stop complaining about) some of those untracked files, and also not add them automatically for tracking-and-committing when you use git add . or git add -A or the like. (Untracked files don't matter for diff and merge, I just mention them for completeness.)

A commit is a permanent, unchangeable snapshot of a tree that comes along with some equally-permanent, equally-unchangeable metadata: someone's name and email address, a time-stamp, a log message, and the notion of a "parent" commit—or, for merge commits, two parents.2

To make a new commit, you use Git's index, which is also called the "staging area" and the "cache". It's basically a place where you build up the next commit. It starts out containing the current commit, and you update files in it, or put new ones in, with git add; then git commit turns the index into a new snapshot-tree, adds the commit metadata—adding your name and email, "now" as the time-stamp, and your log message.

Every commit has a unique hash ID, such as d5c49eb32e898663a8e2c396739d831733e945c2. They're too big and ugly for humans, so we either shorten them, or give them names. There are a bunch of forms for names, but the critical thing is that they always wind up resolving to the hash ID. Those are the "true names".

There is always3 a current commit, which you can name by using the name HEAD. Usually, HEAD actually holds a branch name rather than a raw commit hash ID, and the branch name holds the actual hash ID.

The parent of any new commit is the commit that was current when you ran git commit. That is, the parent of the new commit is whatever HEAD was, and then HEAD takes on the new ID of the new commit. This is true even for new merge commits, even though they have to have at least two parents. The first and "main" parent is from HEAD; the others are from whatever you merged-in.

This is how Git forms the real branches. The branch names are ephemeral, lasting only as long as you care to keep them around. The commits are permanent and unchanging, and each commit records its parent (or parents) ... so we can string together the commits by working backwards, starting from the latest commit.


2Technically, two or more. A merge commit with three or more parents is called an octopus merge. They don't do anything you can't do with regular merges, though.

Also, commits have two name-email-timestamp triples attached: the author, and the committer. For commits you make by yourself, there's not that much difference, but for commits that get rebased or merged or emailed as patches or whatever, these separate "who wrote this commit, and when" from "who actually put it into the repo, and when".

3There is one exception to this rule. Obviously, in a new, empty, repository, there are no commits at all, so there can't be a "current commit". Git generalizes this to something inconsistently called either an orphan branch or an unborn branch, in which HEAD contains the name of a branch, but the branch name itself does not exist anywhere else. Making a new commit while in this state actually creates the branch name. The new commit is a root commit: one with no parents. After that, this weird limbo state is resolved, and new commits get parent commits as usual.


Git draws graphs backwards; finding the merge base

What this all means is that Git (and we) should draw our graphs with branch names at the "newest commit" end, pointing to the tip commit of each branch. When I draw them horizontally I do it like this:

o <- o   <-- master

Here, the name master points to the second and last ("tip") commit on the master branch. That commit points back to the first commit. The first commit, of course, has no previous commit, so it's a root commit and we can stop here.

The internal backwards arrows have little value most of the time, so instead of all this, I just draw these graphs as:

o--o--*--o--o     <-- master
       \
        o--o--o   <-- fix

Newer commits are, as before, on the right, so master and fix—which are branch names—point to the tip commits of their corresponding branch graph fragments.

Now, in this case, I've marked one particular commit with *. This is the nearest common commit when we start from both tips and work backwards. That is, it's the first—in the backwards way Git works—commit that's on both branches. (In graph-theoretic terms, it's a Lowest Common Ancestor of the two vertices in the directed graph, although here again, Git's arcs are backwards from the usual style.)

In any case, this "most recent shared commit" is the actual definition of the merge base. In some complex graphs, there may be more than one merge base, but in simple fork-and-rejoin cases like this one, there's just the one merge base.

Finding the merge base (or bases) is easy if you have a nice clean drawing of the graph. Complicated or messy graphs make it more difficult. Git has a command to find the merge base(s) of particular commits, though:

git merge-base master fix

What this does is resolve master to its commit (the tip of master), and fix to its commit (the tip of fix), and then traverse the graph as necessary to find a merge base.

(If there are several merge bases, it picks one and prints it. To see all of them, add --all. Usually there is only one anyway.)

The quick way to view merge-base diffs

Suppose you want to see what changes git merge will combine when merging master and side. You could do this:

$ git merge-base --all master fix
face0ff1234567...                   # some big ugly hash
$ git diff face0ff master           # see what we changed
$ git diff face0ff fix              # see what they changed

But git diff has a built-in way to find the merge base automatically, using three dots to separate the branch names:

$ git diff fix...master             # see what we did
$ git diff master...fix             # see what they did

This depends on the fact that the merge base search process is symmetric: the merge base of master and fix is the same commit as the merge base of fix and master. So no matter which way we flip the two sides, we get the same base. Git then compares that base to the commit named on the right of this fix...master or master...fix expression.

It's worth noting that this A...B syntax applies to git log as well. In this case, though, it means something different than for git diff. Normally A...B means: Find every commit reachable from A, and every commit reachable from B, and then throw out every commit reachable from both A and B. That is, this produces a symmetric difference (in set theory terms). But git diff can only ever look at two commits, not a whole set; so it steals this syntax to mean something different, that's useful for git diff.

0
Marina Liu On

Based on your git diff output, it’s ok to merge updateCards in master branch. Files compared by the two branchs (+ for updateCards and – for master) as below:

updateCards branch updated files,

update_card_numbers.html

views.py add update_card_numbers(request) function

master branch updated files,

pdf2_base.html
views.py add some new line

So actually, they modified different places. You can merge the two branches with no effect on fixed bugs on updatedCards branch and the new branch you have merged into master.

You can execute git merge updateCards --no-commit before really merge. It will show which files have conflict, the then you can use git merge --abort to stop. When you run git merge updateCards, there will have conflict files, just save what you want to keep, and then use git add filename for each conflict files and git commit.