GitMinorWorkflow

From Mu2eWiki
Revision as of 21:23, 15 December 2017 by Rlc (talk | contribs)
Jump to navigation Jump to search


Introduction

This page describes a recommended git workflow for use with the Mu2e Offline code for minor revisions. It is for small changes, such as changing fcl parameters, fixing bugs, adding minor features, or starting a new module. These modifications are committed directly to the head and should leave the head in a state that compiles and runs. You should work on your local work branch, but after the commit, there should be no branch visible in the main repo.

For more comlex revision use the git major workflow.

This page tells the same story 4 times:

This page presumes that you are familiar with the Mu2e Introduction to git.

If you are already familiar with the background material, you can jump directly to the Cheat Sheet.

Overview

The recommended workflow has the following steps:

  • Start from a clean local master branch that is up to date with the local tracking master branch.
  • Synchronize the master branch in your local repository with that in the redmine repository.
  • Create a local working branch starting from the local master branch. This new branch is temporary and will not appear in the redmine repository.
  • Do your work on the local working branch and make all commits to that branch.
  • Check that there are no uncommitted changes on your work branch.
  • Synchronize your local master branch with the redmine repository.
  • Use git rebase so that your working branch appears as if you had branched it off of the new head of the local master branch. This is the place at which you may need to resolve collisions with changes committed by others.
  • Use git merge to move your working branch onto the head of the local master branch.
  • Use git push to synchronize both the redmine repository and your local master tracking branch with your master branch.
  • Delete your working branch.

The result is that your working branch never appears in the redmine repository; its contents are merged onto to the head of the master branch before that branch is pushed to the redmine repository.

The careful reader will notice that there is a race condition between that last git fetch/pull and the git push; this is addressed in the main body of the discussion.

The above description gives the example of a single working branch but you may many such branches; for example, you might have a separate project underway on each branch. When it is time to rebase and merge one of these branches you can leave the other branches unchanged and continue to work on them at a later time.

Picture Version

This section restates the information above but adds detail and uses figures to help tell the story. The numbered steps correspond to the same numbers in the previous section.

1) Start from a clean local master branch that is up to date with the local tracking master branch.

Check that your master branch is clean

git status

which should produce the output:

# On branch master
nothing to commit, working directory clean

If it does not, consult the page Cleaning your master branch.


2) Synchronize the master branch in your local repository with that in the redmine repository.

There are two ways to perform Step 2. The one step method is to issue the command

git pull

There is also a two step method.

git fetch
git merge --ff-only

There is a discussion of both methods at fetch vs pull.

The following error message may be generated by fetch or pull:

X11 forwarding request failed on channel 0

You can safely ignore it.

For this workflow example we imagine the starting point to be a redmine repository in which there is a master branch that ends in two commits named m1 and m2 (m1 and m2 are mnemonic names for SHA-1 hash codes). In all of these figures, commits are denoted by outline boxes with a white interior and a mnemonic name inside; the arrow pointing from m2 to m1 denotes that m1 is the parent of m2. A git branch is just a lightweight pointer to a commit. In Figure 1 there is one branch, named master, which points to the last commit, m2. In this figure branches are denoted by solid boxes with the branch name inside.

Figure 1.

While the Mu2e redmine repository does contain other branches, none are involved in this workflow example and they are not shown.

After issuing the above command(s), the local repository has been synchronized with the redmine repository. This is illustrated in Figure 2. These figures use the convention that figures representing the redmine repository are shown in blue while those representing the local repository are shown in red.

Figure 2:

The local repository has both a branch named master and a tracking branch named origin/master. Both are shown in Figure 1 and both point to the commit named m2. In most of the upcoming figures, the origin/master branch points to the same commit as does the master branch. When this is the case, only the master branch is shown. When this is not the case, both master and origin/master will be drawn on the figure. Edit 3) Create a local working branch starting from the local master branch.

git checkout -b work


Figure 3 shows the state of the local master branch after Step 3; a new solid red box has been added to represent the newly created working branch; in this example the name of the branch is simply work.

Figure 3:

The redmine repository master branch and the local master tracking branch are unchanged by Step 3. Edit 4) Do your work on the local working branch and make all commits to that branch.

Now suppose that you make 4 commits, named w1 through w4. Figure 4 shows the state of the local repository after those commits: there are four new commits; the local master branch still points at the commit m2; the work branch now points at commit w4.

Figure 4: Edit 5) Check that there are no uncommitted changes on your work branch.

git status


If there are uncommitted changes, commit them now. If the output does not say that your checked out branch is work, consult an expert. Edit 6) Synchronize your local master branch with the redmine repository.

git checkout master git pull


An alternative to the last step is to use git fetch and git merge; see fetch vs pull.

For purposes of this example, suppose that, after you synchronized with redmine repository in Step 2, someone pushed commits m3 and m4 to the master branch in redmine repository. The state of the redmine repository is illustrated in Figure 5:

Figure 5:

Two new commits have been added and the master branch now points at the commit named m4. This figure is drawn in blue to remind you that it represents the stat of the redmine repository, not the local repository.

In step 6, you synchronize the branches master and origin/master in your local repository with the redmine repository. After this step, your local repository will look like Figure 6:

Figure 6

The commits m3 and m4 have been added and the master tag has been moved to point at m4; the origin/master tag has also been moved to point at m4.

In this picture it becomes clear why we use the name "branch" for the master and work branches. Edit 7) This step is the key to the workflow and its action is to transform Figure 6 into Figure 7. This step in called rebasing.

A full discussion of the commands you need to issue at this time don't fit in this section; for the full discussion see the discussion of Step 7 in the last section on this page. The sequence of commands begins with:

git checkout work git rebase master

Figure 7 shows the local repository after rebasing:

Figure 7

Note that the commits in the work branch have new names: w1 became w1-prime, and so on. In words, Step 7 tells git:

   Look up the changes need to transform commit m2 into commit w1
   Apply these changes starting at m4
   Call the result a new commit named w1-prime.
   Repeat for the changes needed to transform w1 to w2, w2 to w3 and w3 to w4.

Very often this "just works" but there are times when it does not. An example of when it will "just work" is when the set of files modified by commits m3 and m4 has no overlap with the set of files modified by w1 through w4. In this case it is said that there are no conflicts.

An example of when rebasing will not work is when both commits m3 and w1 changed the same line of one file and that the two commits made different changes. In this case git does not know what to do and will ask for your help - this case will be described in more detail below. This situation is called and unresolveable conflict.

There are also resolveable conflicts. An example might be when commit m3 deletes one line in a file and commit w1 deletes a different line in the same file. In this case git will produce w1-prime in which both lines are removed.

The git jargon is that rebasing rewrites history to produce the w1-prime through w4-prime that you would have made yourself had you started to work at m4.

If there are problems during rebasing, you can revert to the state prior to the rebase by issuing the command:

git rebase --abort


After a successful rebase the original commits w1, w2, w3 and w4 are still present in your local repository but they are no longer part of any branch. It is possible to recover them but how to do that is out of the scope of this discussion.

Edit 8) Use git merge to move your working branch onto the head of the local master branch.

git checkout master git merge --ff-only work

After this step your local repository will look like Figure 8:

Figure 8

Note that the origin/master branch still points at m4 but that the master branch now points to w4-prime; this is the only figure in the workflow in which master and origin/master point at different commits. If, at this point, you give the git status command, it will tell you that master is 4 commits ahead of origin/master. Edit 9) Use git push to synchronize both the redmine repository and your local master tracking branch with your master branch.

git push


After this step, the local repository looks like Figure 9:

Figure 9

In this figure, master and origin/master once again point at the same place. Figure 10 shows the state of the redmine repository after the push; it does not have the work branch but it is otherwise identical to the local repository.

Figure 10 Edit 10) Delete your working branch.

git branch -d work


Figure 11 shows the state of the local repository after step 10, deleting the work branch.

Figure 11

To summarize this story: there was an intermediate state (Figure 6) when there was a manifest branch in the commit history. But, by the right use of git rebase and git merge, this branch was removed before the push to the redmine repository. The net result is that observers of the redmine repository will see a linear commit history. Edit Cheat Sheet

This section states the workflow with minimal comments; the sections below will describe it in detail. The workflow contains some redundant git status and git checkout commands; they are there to remind you to make sure that you have not forgotten a commit or that you have not wandered off to an incorrect branch. The numbered steps follow the same numbering scheme as the sections above.

   Make sure that the local master branch is clean and is up to date with the local master tracking branch.
   git status
   git checkout master
   git status
   Synchronize the local master branch with the redmine repository
   git pull


   or
   git fetch
   git merge --ff-only origin/master


   For a discussion about the differences between these two options see fetch vs pull.

The following error message may be generated by fetch or pull:

X11 forwarding request failed on channel 0


You can safely ignore it.

   Create a working branch (the branch will be temporary and the name is not important):
   git checkout -b work
   Do your work. Edit files; add/delete/rename files. Commit all of your work
   git add newfile1
   git commit -m  "comment" newfile1 oldfile1 oldfile2
    ... and so on until your work is done ...
   Make sure you have no uncommitted changes
   git status
   Go back to your master branch and sync with the redmine repository
   git checkout master
   git pull


   or
   git checkout master
   git fetch
   git merge --ff-only origin/master


   For a discussion about the differences between these two options see fetch vs pull.
   Rebase. This step is not necessary if git pull did not download any commits.
   git checkout work
   git rebase master         
    ... Git issues an message saying that there is a merge conflict
    ... You need to hand edit the indicated file to resolve the conflict.
   git add hand-edited-file
   git rebase --continue
    ... Git issues an message saying that there is a merge conflict
    ... You need to hand edit the indicated file to resolve the conflict.
       If there are no merge conflicts the git rebase command will complete the first time it is issued.
   Merge your rebased working branch into the master branch
   git checkout master
   git merge --ff-only work
   Push your commits to the redmine repository
   git push
   Delete the working branch
   git branch -d work

There is a race condition between the last git pull and the git push; it will be discussed below. Edit Detailed Version Edit Step 1) Start from a clean local master branch that is up to date with the local tracking branch.

Check the status of your working environment:

git status

The output must look like the following:

  1. On branch master

nothing to commit, working directory clean

If the output is anything else, you have work to do before you can proceed. The steps you need to do are described on the wiki page Cleaning your master branch. Edit Step 2) Synchronize Local Master with the Redmine Repository

This step is not required but it is strongly recommended. If you do it, it will minimize the work that you need to do when you get to the rebase step. To do this, issue the git command:

git pull


or

git fetch git merge --ff-only origin/master


For a discussion about the differences between these two options see fetch vs pull.

Edit Step 3) Create a working branch

You will ultimately merge your working branch into the master branch and the existence of the working branch will be lost to history. Therefore the name of the branch is not important and we will call it work. If the intention is that the branch will become part of the git history, then you should give it a better name. To create and checkout the working branch issue the following git command:

git checkout -b work

In several places the Mu2e git instructions caution you against checking out a branch when you have uncommitted changes in your working tree - this may cause the loss of those changes. There is one prominent exception to this recommendation. If you have uncommitted changes on your master branch it is safe to create a new branch; in this case the uncommitted changes will remain in your working tree and, if you commit them, they will be committed to the new branch.

Now use the git branch command to look at the local branches:

git branch


which will make the output:

 master
  • work


You can see that there are now two local branches; the position of the asterisk tells you that the working tree is an image of the branch named work.

Edit Step 4) Do your work and commit it

Do the edits that you plan to make. Create new files as needed. Then follow the instructions in Wiki for adding files, deleting files, moving files and committing all of these changes.

For this example I removed the CVS meta-data comments from the file SConscript. To be specific I removed the following 4 lines:

  1. $Id: SConstruct,v 1.54 2014/08/02 05:23:26 gandr Exp $
  2. $Author: gandr $
  3. $Date: 2014/08/02 05:23:26 $

Now, check the status:

git status


which produces the output

  1. On branch work
  2. Changes not staged for commit:
  3. (use "git add <file>..." to update what will be committed)
  4. (use "git checkout -- <file>..." to discard changes in working directory)
  5. modified: SConstruct

no changes added to commit (use "git add" and/or "git commit -a")


This tells you that you have modified the file SConstruct but have not yet committed it.

You can see all of the differences between the working tree and its parent branch by using the command:

git diff


If you have modified many files, you can check the changes in just the file SConstruct by giving the command:

git diff SConstruct

The next step is to commit the change:

git commit -m "Remove CVS comments which are no longer needed." SConstruct


which produced the output

[work bac5d3c] Remove CVS comments which are no longer needed.

1 file changed, 1 insertion(+), 5 deletions(-)

Edit Step 5) Check that there are no uncommitted changes

Double check that there are no uncommitted changes:

git status

  1. On branch work

nothing to commit, working directory clean


This says that the working tree is clearn

Another way to check for a clean working tree is to use the command

git diff


which will produce no output because the working tree is clean.

You can also compare the working tree to the local master branch

git diff master


Or you can compare it to the local clone of the redmine master branch

git diff origin/master


You do not need to type the "remotes" part of the branch name but you can if you want.

Since the local master and the local clone of the remote master are the same, both of the above diff commands produce:

diff --git a/SConstruct b/SConstruct index 2f520ce..9ff2699 100644 --- a/SConstruct +++ b/SConstruct @@ -2,10 +2,6 @@

#
# Build a Mu2e base release or test release.
#

-# $Id: SConstruct,v 1.54 2014/08/02 05:23:26 gandr Exp $ -# $Author: gandr $ -# $Date: 2014/08/02 05:23:26 $ -#

# Original author Rob Kutschke.
#
import os, re, string

In this example I only changed one file. You may change many files, add many files, delete many files or rename many files. Just be sure to commit all changes. You may make commit each file individually, commit everything with one big commit or do something in between. Normally it makes sense to commit groups of files that together make one logical change.

The most important thing is to use git status to check that your working tree is clean before proceeding to the next step. Edit Step 6) Sync the local master branch with redmine

First, checkout the local master branch. This changes your working tree to be an image of the local master branch and discards any uncommitted changes to tracked files.

git checkout master

You can double check that you are on the right branch using git branch, which produces the ouptut

  • master
 work

The main part of this step is to use git pull to:

   Contact the redmine repository and update the local tracking branch origins/master.
   Merge the changes from origins/master into the local master

Because we have made not changes to the local master branch, the merge is guaranteed to work without conflicts. Issue the command:

git pull


If there have not been any changes committed to the redmine repository since you did your previous git pull, this will produce the output:

Already up-to-date.

If there have been changes to the redmine repository, the git pull command will produce output like:

Fixme:

.... fill in an example ...

In either case the working tree should be the checked out master branch. Check it with git status, which should show:

  1. On branch master

nothing to commit, working directory clean

Fixme: link to a page discussing git fetch and git merge as an alternative to git pull Edit Step 7) Rebase the working branch

This step is the meat of the entire workflow. It is the place at which conflicts might arise; if they do arise they need to be resolved before moving on.

If the git pull in Step 6) told you that your local repository was already up to date, then this step will do nothing. You can choose to execute it, just to be safe; or you can skip it.

Switch back to your working branch and issue the rebase command.

git checkout work git rebase master


The action of the rebase command is described in the figures earlier on this page. If this command completes successfully, there were no unresolvable conflicts and this step is done. You can proceed to Step 8).

If there is an unresolvable conflict, git rebase will stop and issue a diagnostic telling the name of a file that contains a conflict. You should edit the the file to fix the conflict. When that is done, give the git command

git add relative-path-to-file-that-you-fixed git rebase --continue

If there is another unresolvable conflict fix that by hand and repeat the above commands. And so on. Eventually the rebase will complete and you can move on to Step 8. Edit Step 8) Merge the working branch into the master branch

git checkout master git merge --ff-only work


The ff in "--ff-only" tells git to do a "fast forward" merge. A fast forward merge will only work when there have been no changes to the local master branch since the last rebase of the working branch ( or since the creation of the branch if you have not rebased ). Because the previous step was to rebase, the -ff-only is formally redundant but it is a good idea to include the --ff-only option as a safety check that the rebase actually succeeded.

The output of this step should look like:

Updating aa98377..42447ae Fast-forward

SConstruct | 4 ----
1 file changed, 4 deletions(-)

Edit Step 9) Push the results to redmine repository and clean up

Issue the command:

git push


This will push the changes from the local master branch to the redmine repository and also update the local tracking branch. The push command will produce output like:

git push Counting objects: 8, done. Delta compression using up to 4 threads. Compressing objects: 100% (6/6), done. Writing objects: 100% (6/6), 583 bytes, done. Total 6 (delta 4), reused 0 (delta 0) To ssh://p-mu2eofflinesoftwaremu2eoffline@cdcvs.fnal.gov/cvs/projects/mu2eofflinesoftwaremu2eoffline/Offline.git

  aa98377..42447ae  HEAD -> master

In rare cases someone may push to the redmine repository after your last pull but before you push.

Fixme: describe procedure to deal with this.

Fixme: Marc P believes that a push of a single commit is atomic but the push of multiple commits is not. He is not sure what happens if two people initiate a push of multiple commits within a narrow time window. Can it result in a cross-stitched repository? Is this something that gitflow deals with? Edit Step 10) Delete the working branch

git branch -d work


If you made an error in the above and there remain un-merged commits on this branch, then the delete will fail. You will need to figure out what the error is and how to recover from it.

If you want to force the delete of a branch that fails to delete with the -d option, try the -D option:

git branch -D work


The -D option tells git to delete the branch even if the safety checks fail.

In this workflow, the delete is done after the push succeeds. This ensures that the branch is still accessible in the event that there we need to recover from a failed push. Edit Additional Notes

This section has notes that need to find a home in the main body of the text. Edit Scenario 1

   I have a local uncommitted change in file6.txt
   I do a git fetch and discover that file6.txt has already been changed in the redmine repository.
   explore resolving the conflict using stash

git status

  1. On branch master
  2. Your branch is behind 'origin/master' by 1 commit, and can be fast-forwarded.
  3. (use "git pull" to update your local branch)
  4. Changes not staged for commit:
  5. (use "git add <file>..." to update what will be committed)
  6. (use "git checkout -- <file>..." to discard changes in working directory)
  7. modified: file6.txt

no changes added to commit (use "git add" and/or "git commit -a")

git diff origin/master diff --git a/file6.txt b/file6.txt index 978a23e..f318806 100644 --- a/file6.txt +++ b/file6.txt @@ -1,3 +1,3 @@

This is file 6.  Added in the second repo.
Pushed before committing file 5 in the first repo.

-Added this line in the second repo and pushed. +Added this line in first repo. Then git fetch.

git merge --ff-only origin/master Updating 754536d..3c940d3 error: Your local changes to the following files would be overwritten by merge:

   file6.txt

Please, commit your changes or stash them before you can merge. Aborting

git rebase origin/master Cannot rebase: You have unstaged changes. Please commit or stash them.

git merge origin/master Updating 754536d..3c940d3 error: Your local changes to the following files would be overwritten by merge:

   file6.txt

Please, commit your changes or stash them before you can merge. Aborting

git stash Saved working directory and index state WIP on master: 754536d hand corrections to merge in file 7 HEAD is now at 754536d hand corrections to merge in file 7

git status

  1. On branch master
  2. Your branch is behind 'origin/master' by 1 commit, and can be fast-forwarded.
  3. (use "git pull" to update your local branch)

nothing to commit, working directory clean

git merge --ff-only origin/master Updating 754536d..3c940d3 Fast-forward

file6.txt | 1 +
1 file changed, 1 insertion(+)

git status

  1. On branch master

nothing to commit, working directory clean

git stash pop Auto-merging file6.txt CONFLICT (content): Merge conflict in file6.txt

git status
  1. On branch master
  2. Unmerged paths:
  3. (use "git reset HEAD <file>..." to unstage)
  4. (use "git add <file>..." to mark resolution)
  5. both modified: file6.txt

no changes added to commit (use "git add" and/or "git commit -a")

git add file6.txt

git status
  1. On branch master
  2. Changes to be committed:
  3. (use "git reset HEAD <file>..." to unstage)
  4. modified: file6.txt

git commit -m "Hand merge file6" file6.txt

git status

  1. On branch master
  2. Your branch is ahead of 'origin/master' by 1 commit.
  3. (use "git push" to publish your local commits)

nothing to commit, working directory clean

gitk shows a linear history

git stash list stash@{0}: WIP on master: 754536d hand corrections to merge in file 7

git stash drop Dropped refs/stash@{0} (4b3c222fd438f2f0c886ddebbce760143d75fbd2)

git stash list

( no output )

Edit Scenario 2

   I have a local committed change in file7.txt
   The redmine repo has a conflicting change to file7.txt
   I do a git pull

This will trigger a merge that will put conflict markers in file7.txt. To recover

   edit file7.txt
   git add file7.txt
   git commit -m "hand corrections to merge in file 7" -a
   git push
   Explore resolving the conflict using rebase

This makes a loop in the redmine repository.


caption
caption
caption
caption
caption
caption
caption
caption
caption
caption