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
?
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
andgit 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 extendstip1
.For instance, if you are on branch
main
and you're merging in branchfix
,tip1
is whatever commit-IDmain
resolves to, andtip2
is whatever commit-IDfix
resolves to. Once the merge (as a verb) finishes, you'll make a new merge-commit—this is "merge" as an adjective—onmain
, so thatmain
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 .
orgit 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
; thengit 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 whateverHEAD
was, and thenHEAD
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 fromHEAD
; 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:
Here, the name
master
points to the second and last ("tip") commit on themaster
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:
Newer commits are, as before, on the right, so
master
andfix
—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:
What this does is resolve
master
to its commit (the tip ofmaster
), andfix
to its commit (the tip offix
), 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 mergingmaster
andside
. You could do this:But
git diff
has a built-in way to find the merge base automatically, using three dots to separate the branch names:This depends on the fact that the merge base search process is symmetric: the merge base of
master
andfix
is the same commit as the merge base offix
andmaster
. 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 thisfix...master
ormaster...fix
expression.It's worth noting that this
A...B
syntax applies togit log
as well. In this case, though, it means something different than forgit diff
. NormallyA...B
means: Find every commit reachable fromA
, and every commit reachable fromB
, and then throw out every commit reachable from bothA
andB
. That is, this produces a symmetric difference (in set theory terms). Butgit diff
can only ever look at two commits, not a whole set; so it steals this syntax to mean something different, that's useful forgit diff
.