Nuxeo Core Developer Guide

Git Usage

Updated: December 24, 2024

Prerequisites: having Git installed. For installation and configuration instruction, please refer to Installing Git.

Overview and Online Documentation

http://rogerdudler.github.io/git-guide/: The simple guide

http://git-scm.com/documentation: Very complete documentation (reference, cheat sheets, books).

http://gitready.com/

https://git.wiki.kernel.org/index.php/InterfacesFrontendsAndTools: Interfaces, frontends and tools listing.

Git Commands

Most Useful Commands

To clone a repository, do:

git clone git://github.com/nuxeo/nuxeo.git

To retrieve remote changes, do:

git fetch

To update your repository according to remote changes, do:

git pull --rebase

This is equivalent to:

git fetch && git rebase

To view the current status of your repository, do:

git status

To commit local changes to a file, do:

git commit -m "NXP-xxxx: my commit message" myfile.txt

or, to commit all modified files at once:

git commit -m "NXP-xxxx: my commit message" -a

To push those changes remotely, do:

git push

To view the changeset of a given commit, do:

git show <commit_identifier>

To apply a given changeset to another branch, do:

git cherry-pick <commit_identifier>

To edit the last commit:

git commit --amend [...]

Suppose you made a mistake and made a commit with a bad content, and you forgot to reference the JIRA issue in the message:

echo "my awesome content, i hope i did not make any missstake" > some_file.txt
git commit -m "adding my awesome information" some_file.txt

To fix it just edit the file to replace the faulty line:

echo "my awesome content, i hope i did not make any mistake" > some_file.txt

Then redo the commit with the right commit message:

git commit --amend -m "NXP-xxxx: adding my awesome information" some_file.txt

To undo the last commit (without having pushed anything to public repo), do:

# Cancel last commit but keep changes to be commited
git reset --soft HEAD~

# Cancel last commit and keep changes but not marked for commit
git reset --mixed HEAD~

# Cancel last commit and discard changes
git reset --hard HEAD~

To merge a branch

git merge <branch> [--no-ff]

To rebase the current branch with its upstream branch The local commits can be reworked (squash, edit, move...) during the interactive mode.

git rebase [-i] <upstream>

To revert some changes

git revert <commit>...

Useful Aliases

# Before Git 1.8.3 (May 24, 2013), remove the occurrences of `%C(auto)` or replace them with fixed colors such as `%Cgreen`, `%C(bold blue)`...

# Simple shortcuts
ls = "ls-tree --name-only"
ll = "ls-tree -l"
st = status -sb
ci = commit
co = checkout
br = branch
branches = branch -a
di = diff
dic = diff --staged
id = show -s --pretty=format:'%C(auto)%h%d'
rollback = git reset --soft HEAD^
# Because "pull --rebase" and "rebase" are finally more used than "pull" and "merge"
pullr = "!git fetch \"$@\" && git rebase --autostash @{push}"
# Because we sometimes really want a merge
pullnor = pull --no-rebase

# Amend a changeset (less powerful but quicker and easier than the interactive rebase)
 fix = "!_() { c=$(git rev-parse $1) && git commit --fixup $c && git -c core.editor=cat rebase -i --autosquash --keep-empty --autostash $c~; }; _"

# Using ref to upstream branch available since Git 1.7 (@{upstream})
# Incoming changes
in = "!git remote update -p; git log ..@{u}"
# Outgoing changes
out = log @{u}..
# Outgoing changes on all remote-tracked branches
outall = log --branches --not --remotes=origin
# Branch fork point (aka oldest ancestor)
oldest-ancestor = !bash -c 'diff -u1 <(git rev-list --first-parent "${1:-master}") <(git rev-list --first-parent "${2:-HEAD}") | sed -ne \"s/^ //p\"' -

# Various log display
lg = log --graph --pretty=format:'%C(auto)%h -%d %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit
glog = log --graph --abbrev-commit --date=relative
logone = log --pretty=format:'%C(auto)%m %h %Cgreen%ad %C(blue)%<(20)%aN %Creset%s %N %C(auto)%d' --left-right --cherry-pick --date=short
# Latests changes since last pull
lc = log --pretty=oneline --abbrev-commit --graph --decorate ORIG_HEAD.. --stat --no-merges
# Latest updated branches
latest = for-each-ref --count=10 --sort=-committerdate --format='%(committerdate:short) %(refname:short)'
# Latest updated local branches
latestl = for-each-ref --count=10 --sort=-committerdate refs/heads --format='%(committerdate:short) %(refname:short)'

# Commits per user
who = "shortlog -ne --format='%h %s'"
# LoC per user (BSD)
authorship = "!git ls-files -z|xargs -0 -n1 -E'\n' -J {} git blame --date short -wCMcp '{}'| perl -pe 's/^.*?\\((.*?) +\\d{4}-\\d{2}-\\d{2} +\\d+\\).*/\\1/'| sort | uniq -c | sort -rn"
# LoC per user (GNU)
authorship = "!git ls-files -z|xargs -0 -n1 -E'\n' | git blame --date short -wCMcp -- | perl -pe 's/^.*?\\((.*?) +\\d{4}-\\d{2}-\\d{2} +\\d+\\).*/\\1/'| sort | uniq -c | sort -rn"

rbranch-rename = "!_() { [ \"$#\" -lt 2 ] && echo 'Usage: rbranch-rename [remote] old_branch_name new_branch_name' && exit 1; \
                       if [ \"$#\" -gt 2 ]; then REMOTE=$1; shift; else REMOTE=origin; fi; git push $REMOTE $REMOTE/$1:refs/heads/$2 :$1; }; _"

# See https://gist.github.com/jcarsique/24f8dd46d176bb67253e for more aliases.

Command Mappings for a Mercurial User

Here are simple commands and correspondences between Mercurial (we were used to) and Git:

See Git Hg rosetta stone.

Show file content in a given revision <=> hg cat

git show ref[:file]...

Apply the changes introduced by some existing commits <=> hg transplant

git cherry-pick <commit>...

Undo last commit (without having pushed anything to public repo) <=> hg rollback

git reset --soft HEAD~1

Nuxeo Common Usage and Best Practices

Convenient Shell Scripts

At root of the Nuxeo Platform, the script scripts/gitfunctions.sh provides some convenient Shell functions for use on Nuxeo projects version controlled under Git.

source <(curl -s https://raw.githubusercontent.com/nuxeo/nuxeo/stable/scripts/gitfunctions.sh)
# or source /path/to/nuxeo/scripts/gitfunctions.sh

gitf -?
Usage: gitf [-r] [-q] [-h|-?] <[--] Git instructions>
Recursively executes the given Git command on the current and its direct children Git repositories.

    -h,-?    Show this help message and exit.
    -q    Quiet mode (default is verbose).
    -r    The recurse will continue on all children Git repositories.
    --    Optional separator between the options and the instructions which may start with a dash.

gitfa -?
Usage: gitfa [-q] [-h|-?] <[--] Git instructions>
Recursively executes the given Git command on the current, its direct children Git repositories and browse the child Maven modules of $PARENT_MODULES directories.

    -h,-?    Show this help message and exit.
    -q    Quiet mode (default is verbose).
    --    Optional separator between the options and the instructions which may start with a dash.
    PARENT_MODULES Environment variable. Defaults to 'addons addons-core' if not set.
    LIST_<parent module> Environment variable that can be set to restrict the children modules to a fixed list. For instance: LIST_ADDONS_CORE="nuxeo-core-storage-marklogic" or LIST_ADDONS="nuxeo-shell nuxeo-quota".

shr -?
Usage: shr [-a] [-q] [-h|-?] <[--] Shell instructions>
Recursively executes the given Shell command on the current and its direct children Git repositories.

    -h,-?    Show this help message and exit.
    -a    The recurse will continue on the child Maven modules of $PARENT_MODULES directories.
    -q    Quiet mode (default is verbose).
    --    Optional separator between the options and the instructions which may start with a dash.
    PARENT_MODULES Environment variable. Defaults to 'addons addons-core' if not set.
    LIST_<parent module> Environment variable that can be set to restrict the children modules to a fixed list. For instance: LIST_ADDONS_CORE="nuxeo-core-storage-marklogic" or LIST_ADDONS="nuxeo-shell nuxeo-quota".

Main Rules and Good Practices

  • Do not rewrite Git history on development and maintenance branches.
  • Branch naming
    • Strictly follow Nuxeo naming policy for branches and tags
    • Tags are named release-<semver>
    • Create working branches named as <TYPE>-<REFERENCE>-<SHORT_DESC>: fix-NXP-1234-some-expected-behavior or "feature-NXP-1234-some-new-feature
    • Use the same name for a local branch than its corresponding remote branch.
  • Commits
    • Reference JIRA issues in the head of commit comment.
    • Comment with a first short line, then add optionally an empty line, then as much details as you consider useful
    • When squashing other author's commits, typically during a PR, mentions are welcome in the commit comment body.
      For instance:
      Co-authored-by: @username <[email protected]>
      Reviewed-by: @reviewer_username <[email protected]>
      
    • Commit as a valid author (i.e. "Firstname Lastname \" with the email address being configured in your GitHub account, no matter if it's public or private).
  • Merging work
    • Rebase against the upstream then ask for code review with a Pull Request
    • Consider reworking the branch before review: isolate code cleanup, squash related commits, etc.
    • When your work is achieved, rebase again then merge the branch (see Isolated testing with ondemand-testandpush jobs).
    • Unique and very few commits can be merged as fast-forward, else creating a merge commit is recommended for readability.
    • Optionally delete the remaining branch, else it will be automatically done after a short while once the related JIRA ticket is resolved.

Advanced Usage and Specific Use Cases

Some Useful Commands

Adding a remote repository to a local Git repository

git remote add origin [email protected]:nuxeo/nuxeo.git

Pushing to master branch on remote repository with alias origin

git push -u origin master

Renaming a branch

# Rename local branch
git branch -m <oldname> <newname>

# Rename remote branch. See 'rbranch-rename' alias.
git push origin origin/<oldname>:refs/heads/<newname> :<oldname>

Removing a local branch

git branch -d branch_name

Removing a remote branch

# Old way
git push origin :branch_name
# New way
git push --delete branch_name

Removing old (stale) remote branch references

git remote prune <remote_repository_alias>

Creating an annotated tag

git tag -a <tag_name> <sha1> -m"<tag_message>"

Removing a tag

git tag -d <tag_name>

Pushing a tag

git push <remote_repository_alias> <tag_name>

Removing a remote tag

git push <remote_repository_alias> :refs/tags/<tag_name>

Checking Out Pull Request Locally

Sometimes, a pull request needs local changes (to resolve merge or rebase conflicts, to fix or complete the commits, to run tests...). You can get GitHub instructions at the bottom of the pull request page, near the merge button: click on "command line instructions". Here is a summary of those instructions and some alternate commands.

Let's assume that the user name is contributor and he's issuing the pull request number #67 from the branch fix-NXP-12345-some-stuff in his forked repository https://github.com/contributor/nuxeo.git to branch master in http://github.com/nuxeo/nuxeo.git.

As a Simple Patch

For simple cases, you can locally apply the pull-request as a patch (see GitHub documentation):

git checkout -b fix-NXP-12345-some-stuff master
curl -L https://github.com/nuxeo/nuxeo/pull/67.patch | git am

As a Branch

# Fetch details are needed only if you didn't configure remote.origin.fetch at global or repository level
git fetch origin +pull/67/head:pull/67

# Pull requests can be checked out like a standard branch
git checkout pull/67

# You can list the modified files and open them in your preferred IDE (here using Eclipse for a PR on master)
git diff origin/master --name-only --format="" --diff-filter=d | xargs eclipse

Fetch the Pull Request Changes

git checkout -b fix-NXP-12345-some-stuff master
git pull origin pull/67/head

Fetch the Pull Request Changes (Alternative)

Alternative command fetching from the contributor's repository instead of fetching from the pull request

git checkout -b fix-NXP-12345-some-stuff master
git pull https://github.com/contributor/nuxeo.git fix-NXP-12345-some-stuff

Removing Content From Git History

This action modifies the Git history! All commits previously containing the unwanted files will be rewritten.

Removing files from Git history

git filter-branch --index-filter 'git rm -r --cached --ignore-unmatch files_to_remove' HEAD
git rm -r --cached files_to_remove
git commit

See also BFG Repo-Cleaner which provides very much faster and somehow simpler (user-friendlier) commands for a few high-level features.

Converting a Mercurial Repository to a Git Repository

The conversion makes use of hg-fast-export:

Migrating from Mercurial to Git

git clone git://repo.or.cz/fast-export.git
git -C fast-export checkout v180317 # Backward compatibility with Mercurial < 4.6
git init new-git-repository
cd new-git-repository
/path/to/fast-export/hg-fast-export.sh -r /path/to/old-hg-repository

More info in this blog post.

Post migration cleanup

for i in `git branch | grep -v master`; do git log -1 --pretty="format:$i%x09%s%n" $i | grep '[Tt]ag[g ]' | cut -f 1; done | while read i; do (git branch -d $i||true); done
git checkout master # the current working branch is out of date after conversion

Notes/tips:

  • "default" Mercurial branch will be mapped to "master" Git branch. If you were not using the "default" branch but for instance "5.5" or if the "master" should start pointing at "5.5", do:

    git checkout -B master 5.5
    # Optionally commit the new branch creation after having updated its content (see below).
    
    

    Moving .hgignore to .gitignore

    git mv .hgignore .gitignore
    # Edit and remove non-Git configuration from .gitignore, such as "syntax:glob".
    git commit -m"NXP-XXXX: migrate from Hg to Git" -a
    
    

    Adding remote repository; pushing branches and tags

    git remote add origin [email protected]:nuxeo/some_repository.git
    git push --all origin
    git push --tags
    git branch --set-upstream-to=origin/master master
    

  • Set the Mercurial repository as deprecated.
  • Activate GitHub hooks after having pushed the migration.

Extracting Part(s) of an Existing Repository to a New Repository

Clone the repository, then:

For one directory:

git filter-branch --subdirectory-filter dir-to-extract --tag-name-filter cat --prune-empty -- --all

For multiple directories:

git filter-branch --index-filter 'git rm  --ignore-unmatch --cached -qr -- . && git reset -q $GIT_COMMIT -- dir1-to-extract dir2-to-extract dir3-to-extract' --tag-name-filter cat --prune-empty -- --all

Finally:

for i in $(git branch -r | sed "s/.*origin\///"); do git branch -t $i origin/$i; done
git remote rm origin
git reset --hard
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
git reflog expire --expire=now --all
git gc --aggressive --prune=now

The new repository is ready for push.

You can remove the extracted directory from the old repository (git rm ...).

Merging Two Repositories

See for a workaround to the issue https://github.com/robinst/git-merge-repos/issues/3

Prepare script is available: https://gist.github.com/jcarsique/29ca0df9166926183e2f

If you work on a local clone, you must remove all local branches or ensure that they are up-to-date. See http://aanandprasad.com/git-up/.

There are two main merging solutions:

  • Merge history for paths: the commits are rewrote so that everything is as if the two repositories were merged since their beginning.
  • Merge branch heads: the commit IDs are unchanged. The branches existing in the two repositories are merged at their HEAD. History for each repository is kept unchanged.

On open source projects, with commit IDs referenced from public issues, the latter solution is preferred.

Clone https://github.com/robinst/git-merge-repos

Run ./run.sh /path/to/repo1:. /path/to/repo2:..

The result is in a sub-directory named merged-repo. Add the remote and push the branches without force option, then the tags.

# Update .gitignore and remove those in sub-directories
git remote add origin ...
# Optimize repository
git reflog expire --expire=now --all
git gc --aggressive --prune=now
git fsck
# Check outgoing changes
git push --all -n origin 2>&1 | grep -E "deleted|rejected"
# Merge remote changes per branch if necessary; see also git-up command to update all local branches
# Push changes
git push --all origin
git push --tags -f origin

If you need to rebase (or pull --rebase) some local branch (after repositories merge), then you must use the rebase --preserve-merges option (or pull --rebase=preserve).

Please disable GitHub email notifications before push and re-enable after push.

After the merge, the Jenkins job running on your repository will probably add comments on JIRA issues referenced in the rewritten history: these comments could be nice to have, but if you do not want them, you should disable the "Update relevant JIRA issues" options on the job, at its first run after the merge.

Using Another Local Clone as Staging

You may want to push from merged-repo to a local clone of foo:

cd /path/to/foo
git checkout --detach
git fetch --tags /path/to/merged-repo
cd /path/to/merged-repo
git push --all /path/to/foo

The foo repository is now ready for push to remote. Of course, you will often want to finalize the merge first (add modules to a common POM, remove useless .gitignore...)

If there was a remote conflicting change, then you can rebase the new branch(es).

If you had local changes in foo, then you may encounter conflicts at push:

To /path/to/foo
 ! [rejected]        some-conflicting-branch -> some-conflicting-branch (fetch first)
error: failed to push some refs to '/path/to/foo'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

In that case, you will have to manually solve the conflict: choosing the branch to keep, optionally merging/cherry-picking some commits...

For instance, to forget the local changes:

cd /path/to/foo
git fetch -f /path/to/merged-repo some-conflicting-branch:some-conflicting-branch

Walking Through History After Merge

After merge, the resulting repository will have two distinct history trees that converge into the future unique history. The drawback is that it is not easy to browse the old history of the imported repository.

In the following example, we have merged a repository B under a new folder repo-b/ of the repository A.

$ tree -L 2
.
├── repo-b/      # Imported content of B, shifted.
│   ├── pom.xml
│   └── ...
├── pom.xml
└── ...          # Original content of A, unchanged.

$ git show --summary
commit eb9a482ef762372b7ebc868045aa210a8b553d24
Merge: cafb9624c 943add6f0

    Merge branch 'master' from multiple repositories
    Repositories:
            A
            B

# cafb9624c is the HEAD of the original content (repo A)
# 943add6f0 is the HEAD of the imported content (repo B)

$ git log --graph --pretty=format:'%h -%d %s' --simplify-by-decoration
*   eb9a482ef - (HEAD -> master) Merge branch 'master' from multiple repositories
|\  
| * 3d3ebc4a0 - first commit on B
* cafb9624c - (origin/master) last commit on A

Here are some history browsing Git commands with their behavior regarding the "old" history (before the merge of repositories)

# To walk through both trees, use --follow starting from a commit post-merge

$ git log --oneline --follow -- pom.xml|grep 3d3ebc4a0
3d3ebc4a0 NXP-14853: First commit.

$ git log --oneline --follow -- repo-b/pom.xml|grep 3d3ebc4a0
3d3ebc4a0 NXP-14853: First commit.

$ git log --oneline --follow -- repo-b/pom.xml|wc -l
476
$ git log --oneline --follow -- pom.xml|wc -l
476

# To walk through the old trees, use a pre-merge commit: cafb9624c for repo A and 943add6f0 for repo B

$ git log --oneline cafb9624c --follow -- pom.xml|wc -l
445
$ git log --oneline 943add6f0 --follow -- pom.xml|wc -l
30

# 3d3ebc4a0 is not in the old tree A but in tree B
$ git log --oneline cafb9624c --follow -- pom.xml|grep 3d3ebc4a0
$ git log --oneline 943add6f0 --follow -- pom.xml|grep 3d3ebc4a0
3d3ebc4a0 NXP-14853: First commit.

Normalize End of Lines

Per-Branch Normalization

Git allows to apply automatic checks on end of lines depending on the file attributes (see http://git-scm.com/docs/gitattributes/).

Ensure correct EOL on *.sh and *.bat files

# End-of-line conversion like in https://raw.githubusercontent.com/nuxeo/nuxeo/master/.gitattributes
echo "*.sh eol=lf" >>.gitattributes
echo "*.bat eol=crlf" >>.gitattributes
rm .git/index
git reset
git add -u
git add .gitattributes
git ci -m'Introduce end-of-line normalization'

That will automatically convert end of lines in *.sh and *.bat files in the branch containing the .gitattributes file.

Per-Repository Normalization

You can set such an automatic conversion on all branches with the following:

Automatic EOL conversion on all branches

echo "*.sh eol=lf" >>.git/info/attributes
echo "*.bat eol=crlf" >>.git/info/attributes

The .git/info/attributes file is not committed but local to the repository.

Note however that this configuration will likely cause conflicts on merge (or rebase). In such a case, the easier solution is to temporarily rename .git/info/attributes, end the merge (or rebase), then put back the attributes file.

Global Enforcement

See Installing Git / Git Global Configuration for a setting core.autocrlf, core.safecrlf... parameters to enforce the EOL checks on all Git repositories.

Useful Tools

The following commands are very useful for dealing with EOL: file, unix2dos and dos2unix.

Diff on Binary Files

It is possible to tell Git how to convert binary data to a text format that can be compared via the normal diff.

PNG and DOCX

Here is a sample configuration for .docx and .png files (from http://git-scm.com/book/en/v2/Customizing-Git-Git-Attributes):

  1. Install docx2txt and exiftool
  2. Create a docx2txt wrapper script and mark it as executable:

    ~/bin/docx2txt

    #!/bin/bash
    docx2txt.pl $1 -
    

    chmod a+x ~/bin/docx2txt

  3. Tell Git which binary files to convert:

    Use .git/info/attributes if you don’t want the attributes file committed with your project, else use .gitattributes

    .git/info/attributes or .gitattributes

    *.docx diff=word
    *.png diff=exif
    

  4. Tell Git how to convert:

    git config --global diff.word.textconv docx2txt
    git config --global diff.exif.textconv exiftool
    

Here are the outputs with and without the above Git attributes:

Default diff on docx

$ git show 281e4f470bb0e8fc1d43b9350e72063030d15f66 --oneline  -- nuxeo-core-convert-plugins-test/src/test/resources/test-docs/hello.docx
281e4f4 NXP-9331: Get rid of cyclic dependency by isolating tests in nuxeo-core-convert-plugins-test
diff --git a/nuxeo-core-convert-plugins-test/src/test/resources/test-docs/hello.docx b/nuxeo-core-convert-plugins-test/src/test/resources/test-docs/hello.docx
new file mode 100644
index 0000000..ad1b4af
Binary files /dev/null and b/nuxeo-core-convert-plugins-test/src/test/resources/test-docs/hello.docx differ

Smart diff on docx

$ git show 281e4f470bb0e8fc1d43b9350e72063030d15f66 --oneline  -- nuxeo-core-convert-plugins-test/src/test/resources/test-docs/hello.docx
281e4f4 NXP-9331: Get rid of cyclic dependency by isolating tests in nuxeo-core-convert-plugins-test
diff --git a/nuxeo-core-convert-plugins-test/src/test/resources/test-docs/hello.docx b/nuxeo-core-convert-plugins-test/src/test/resources/test-docs/hello.docx
new file mode 100644
index 0000000..ad1b4af
--- /dev/null
+++ b/nuxeo-core-convert-plugins-test/src/test/resources/test-docs/hello.docx
@@ -0,0 +1 @@
+Hello world !

Default diff on PNG

$ git show 025a2bda872a228d2ee07b8830e2472088ae43a5 --oneline -- nuxeo-platform-webapp/src/main/resources/web/nuxeo.war/img/nuxeo_logo.png
025a2bd NXP-15048: Update logo, flavors and login page for new branding
diff --git a/nuxeo-platform-webapp/src/main/resources/web/nuxeo.war/img/nuxeo_logo.png b/nuxeo-platform-webapp/src/main/resources/web/nuxeo.war/img/nuxeo_logo.png
index a28e89d..b38c0e0 100644
Binary files a/nuxeo-platform-webapp/src/main/resources/web/nuxeo.war/img/nuxeo_logo.png and b/nuxeo-platform-webapp/src/main/resources/web/nuxeo.war/img/nuxeo_logo.png differ

Smart diff on PNG

$ git show 025a2bda872a228d2ee07b8830e2472088ae43a5 --oneline -- nuxeo-platform-webapp/src/main/resources/web/nuxeo.war/img/nuxeo_logo.png
025a2bd NXP-15048: Update logo, flavors and login page for new branding
diff --git a/nuxeo-platform-webapp/src/main/resources/web/nuxeo.war/img/nuxeo_logo.png b/nuxeo-platform-webapp/src/main/resources/web/nuxeo.war/img/nuxeo_logo.png
index a28e89d..b38c0e0 100644
--- a/nuxeo-platform-webapp/src/main/resources/web/nuxeo.war/img/nuxeo_logo.png
+++ b/nuxeo-platform-webapp/src/main/resources/web/nuxeo.war/img/nuxeo_logo.png
@@ -1,15 +1,15 @@
 ExifTool Version Number         : 9.69
-File Name                       : QoqEHp_nuxeo_logo.png
+File Name                       : EYPa9v_nuxeo_logo.png
 Directory                       : /tmp
-File Size                       : 4.9 kB
+File Size                       : 8.0 kB
 File Modification Date/Time     : 2015:01:30 15:39:32+01:00
 File Access Date/Time           : 2015:01:30 15:39:32+01:00
 File Inode Change Date/Time     : 2015:01:30 15:39:32+01:00
 File Permissions                : rw-------
 File Type                       : PNG
 MIME Type                       : image/png
-Image Width                     : 236
-Image Height                    : 52
+Image Width                     : 227
+Image Height                    : 40
 Bit Depth                       : 8
 Color Type                      : RGB with Alpha
 Compression                     : Deflate/Inflate
@@ -18,8 +18,8 @@ Interlace                       : Noninterlaced
 Software                        : Adobe ImageReady
 XMP Toolkit                     : Adobe XMP Core 5.3-c011 66.145661, 2012/02/06-14:56:27
 Creator Tool                    : Adobe Photoshop CS6 (Macintosh)
-Instance ID                     : xmp.iid:659F991F084311E4A7D7A0843068B9FA
-Document ID                     : xmp.did:659F9920084311E4A7D7A0843068B9FA
-Derived From Instance ID        : xmp.iid:659F991D084311E4A7D7A0843068B9FA
-Derived From Document ID        : xmp.did:659F991E084311E4A7D7A0843068B9FA
-Image Size                      : 236x52
+Instance ID                     : xmp.iid:394FADE624C911E48679ADB8C76F05E6
+Document ID                     : xmp.did:394FADE724C911E48679ADB8C76F05E6
+Derived From Instance ID        : xmp.iid:EBA4519B23A511E48679ADB8C76F05E6
+Derived From Document ID        : xmp.did:EBA4519C23A511E48679ADB8C76F05E6
+Image Size                      : 227x40

ZIP

```
git config --global diff.zip.textconv "unzip -c -a"
echo "*.zip diff=zip" >> .gitattributes
```

Git Large File Storage

GitHub will warn you when pushing files larger than 50 MB. You will not be allowed to push files larger than 100 MB. Actually, storing binaries in Git is a bad practice. The usage pattern should be reviewed in order to fetch external data from Nexus for instance.

However, if you think you legitimately need to store (fat) binaries in Git, then there are solutions reducing the inconveniences: git-annex, git-fat, Git LFS, git-bigfiles, git-media, git-bigstor, git-sym... The three firsts being the most promising. Nuxeo choose Git LFS: https://git-lfs.github.com/

Configuration

The following should avoid running git lfs install on each Git repository.

git config --global filter.lfs.required true
git config --global filter.lfs.clean "git-lfs clean %f"
git config --global filter.lfs.smudge "git-lfs smudge %f"

Batch Download

LFS has the batch mode for downloading files from a server. But it does not work on clone and checkout due to limitation of Git’s smudge filters. You can skip LFS’s smudge filter and fetch LFS objects on demand:

# change the smudge filter configuration
git config --global filter.lfs.smudge "git-lfs smudge --skip %f"
# check
git lfs env

# fetch LFS objects after checkout or clone:
git lfs fetch # downloads objects with batch mode
git lfs checkout # changes objects to binary files

Issues

Commands naming is not consistent between Git and Git LFS!

Git LFS does not seem very stable yet and it has some inconsistencies with Git when mixing LFS and Git-native binaries. You may encounter modified or untracked binaries while you didn't change them.

See https://shuhrat.github.io/programming/git-lfs-tips-and-tricks.html, https://github.com/tarmolov/git-hooks-js to configure and share a safety pre-commit hook.

See https://github.com/bozaro/git-lfs-migrate to migrate an existing repository.