To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

Statistics Download as Zip
| Branch: | Tag: | Revision:

root / app / models / repository / git.rb @ 912:5e80956cc792

History | View | Annotate | Download (6 KB)

1 441:cbce1fd3b1b7 Chris
# Redmine - project management software
2
# Copyright (C) 2006-2011  Jean-Philippe Lang
3 0:513646585e45 Chris
# Copyright (C) 2007  Patrick Aljord patcito@ŋmail.com
4 441:cbce1fd3b1b7 Chris
#
5 0:513646585e45 Chris
# This program is free software; you can redistribute it and/or
6
# modify it under the terms of the GNU General Public License
7
# as published by the Free Software Foundation; either version 2
8
# of the License, or (at your option) any later version.
9 441:cbce1fd3b1b7 Chris
#
10 0:513646585e45 Chris
# This program is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
# GNU General Public License for more details.
14 441:cbce1fd3b1b7 Chris
#
15 0:513646585e45 Chris
# You should have received a copy of the GNU General Public License
16
# along with this program; if not, write to the Free Software
17
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18
19
require 'redmine/scm/adapters/git_adapter'
20
21
class Repository::Git < Repository
22
  attr_protected :root_url
23
  validates_presence_of :url
24
25 245:051f544170fe Chris
  def self.human_attribute_name(attribute_key_name)
26 441:cbce1fd3b1b7 Chris
    attr_name = attribute_key_name
27
    if attr_name == "url"
28
      attr_name = "path_to_repository"
29
    end
30
    super(attr_name)
31 245:051f544170fe Chris
  end
32
33
  def self.scm_adapter_class
34 0:513646585e45 Chris
    Redmine::Scm::Adapters::GitAdapter
35
  end
36 245:051f544170fe Chris
37 0:513646585e45 Chris
  def self.scm_name
38
    'Git'
39
  end
40
41 441:cbce1fd3b1b7 Chris
  def report_last_commit
42
    extra_report_last_commit
43
  end
44
45
  def extra_report_last_commit
46
    return false if extra_info.nil?
47
    v = extra_info["extra_report_last_commit"]
48
    return false if v.nil?
49
    v.to_s != '0'
50
  end
51
52
  def supports_directory_revisions?
53
    true
54
  end
55
56 909:cbb26bc654de Chris
  def supports_revision_graph?
57
    true
58
  end
59
60 245:051f544170fe Chris
  def repo_log_encoding
61
    'UTF-8'
62
  end
63
64 117:af80e5618e9b Chris
  # Returns the identifier for the given git changeset
65
  def self.changeset_identifier(changeset)
66
    changeset.scmid
67
  end
68
69
  # Returns the readable identifier for the given git changeset
70
  def self.format_changeset_identifier(changeset)
71
    changeset.revision[0, 8]
72
  end
73
74 0:513646585e45 Chris
  def branches
75
    scm.branches
76
  end
77
78
  def tags
79
    scm.tags
80
  end
81
82 507:0c939c159af4 Chris
  def default_branch
83
    scm.default_branch
84 909:cbb26bc654de Chris
  rescue Exception => e
85
    logger.error "git: error during get default branch: #{e.message}"
86
    nil
87 507:0c939c159af4 Chris
  end
88
89 245:051f544170fe Chris
  def find_changeset_by_name(name)
90
    return nil if name.nil? || name.empty?
91
    e = changesets.find(:first, :conditions => ['revision = ?', name.to_s])
92
    return e if e
93
    changesets.find(:first, :conditions => ['scmid LIKE ?', "#{name}%"])
94
  end
95
96 441:cbce1fd3b1b7 Chris
  def entries(path=nil, identifier=nil)
97
    scm.entries(path,
98
                identifier,
99
                options = {:report_last_commit => extra_report_last_commit})
100
  end
101
102 909:cbb26bc654de Chris
  # With SCMs that have a sequential commit numbering,
103
  # such as Subversion and Mercurial,
104
  # Redmine is able to be clever and only fetch changesets
105
  # going forward from the most recent one it knows about.
106
  #
107
  # However, Git does not have a sequential commit numbering.
108
  #
109
  # In order to fetch only new adding revisions,
110
  # Redmine needs to parse revisions per branch.
111
  # Branch "last_scmid" is for this requirement.
112
  #
113 441:cbce1fd3b1b7 Chris
  # In Git and Mercurial, revisions are not in date order.
114
  # Redmine Mercurial fixed issues.
115
  #    * Redmine Takes Too Long On Large Mercurial Repository
116
  #      http://www.redmine.org/issues/3449
117
  #    * Sorting for changesets might go wrong on Mercurial repos
118
  #      http://www.redmine.org/issues/3567
119
  #
120
  # Database revision column is text, so Redmine can not sort by revision.
121
  # Mercurial has revision number, and revision number guarantees revision order.
122
  # Redmine Mercurial model stored revisions ordered by database id to database.
123
  # So, Redmine Mercurial model can use correct ordering revisions.
124
  #
125
  # Redmine Mercurial adapter uses "hg log -r 0:tip --limit 10"
126
  # to get limited revisions from old to new.
127
  # But, Git 1.7.3.4 does not support --reverse with -n or --skip.
128
  #
129 0:513646585e45 Chris
  # The repository can still be fully reloaded by calling #clear_changesets
130
  # before fetching changesets (eg. for offline resync)
131
  def fetch_changesets
132 441:cbce1fd3b1b7 Chris
    scm_brs = branches
133
    return if scm_brs.nil? || scm_brs.empty?
134
    h1 = extra_info || {}
135
    h  = h1.dup
136
    h["branches"]       ||= {}
137
    h["db_consistent"]  ||= {}
138
    if changesets.count == 0
139
      h["db_consistent"]["ordering"] = 1
140
      merge_extra_info(h)
141
      self.save
142
    elsif ! h["db_consistent"].has_key?("ordering")
143
      h["db_consistent"]["ordering"] = 0
144
      merge_extra_info(h)
145
      self.save
146
    end
147 909:cbb26bc654de Chris
    scm_brs.each do |br1|
148
      br = br1.to_s
149 441:cbce1fd3b1b7 Chris
      from_scmid = nil
150
      from_scmid = h["branches"][br]["last_scmid"] if h["branches"][br]
151
      h["branches"][br] ||= {}
152
      scm.revisions('', from_scmid, br, {:reverse => true}) do |rev|
153
        db_rev = find_changeset_by_name(rev.revision)
154 245:051f544170fe Chris
        transaction do
155 441:cbce1fd3b1b7 Chris
          if db_rev.nil?
156 909:cbb26bc654de Chris
            db_saved_rev = save_revision(rev)
157
            parents = {}
158
            parents[db_saved_rev] = rev.parents unless rev.parents.nil?
159
            parents.each do |ch, chparents|
160
              ch.parents = chparents.collect{|rp| find_changeset_by_name(rp)}.compact
161
            end
162 245:051f544170fe Chris
          end
163 441:cbce1fd3b1b7 Chris
          h["branches"][br]["last_scmid"] = rev.scmid
164
          merge_extra_info(h)
165
          self.save
166 245:051f544170fe Chris
        end
167
      end
168
    end
169 0:513646585e45 Chris
  end
170
171 441:cbce1fd3b1b7 Chris
  def save_revision(rev)
172
    changeset = Changeset.new(
173
              :repository   => self,
174
              :revision     => rev.identifier,
175
              :scmid        => rev.scmid,
176
              :committer    => rev.author,
177
              :committed_on => rev.time,
178
              :comments     => rev.message
179
              )
180
    if changeset.save
181
      rev.paths.each do |file|
182
        Change.create(
183
                  :changeset => changeset,
184
                  :action    => file[:action],
185
                  :path      => file[:path])
186
      end
187
    end
188 909:cbb26bc654de Chris
    changeset
189 441:cbce1fd3b1b7 Chris
  end
190
  private :save_revision
191
192 0:513646585e45 Chris
  def latest_changesets(path,rev,limit=10)
193
    revisions = scm.revisions(path, nil, rev, :limit => limit, :all => false)
194
    return [] if revisions.nil? || revisions.empty?
195
196
    changesets.find(
197 441:cbce1fd3b1b7 Chris
      :all,
198 0:513646585e45 Chris
      :conditions => [
199 441:cbce1fd3b1b7 Chris
        "scmid IN (?)",
200 0:513646585e45 Chris
        revisions.map!{|c| c.scmid}
201
      ],
202
      :order => 'committed_on DESC'
203
    )
204
  end
205
end